feat(boards): 增加python_skulpt_car板卡
This commit is contained in:
@@ -142,7 +142,7 @@
|
|||||||
"webSocket": true
|
"webSocket": true
|
||||||
},
|
},
|
||||||
"language": "Python"
|
"language": "Python"
|
||||||
},
|
},*/
|
||||||
{
|
{
|
||||||
"boardImg": "./boards/default/python_skulpt/media/webpy.png",
|
"boardImg": "./boards/default/python_skulpt/media/webpy.png",
|
||||||
"boardType": "Python 3 Car",
|
"boardType": "Python 3 Car",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"webSocket": true
|
"webSocket": true
|
||||||
},
|
},
|
||||||
"language": "Python"
|
"language": "Python"
|
||||||
},*/
|
},
|
||||||
{
|
{
|
||||||
"boardImg": "./boards/default/micropython_k210_mixgoai/media/mixgoai_compressed.png",
|
"boardImg": "./boards/default/micropython_k210_mixgoai/media/mixgoai_compressed.png",
|
||||||
"boardType": "MixGo AI",
|
"boardType": "MixGo AI",
|
||||||
|
|||||||
@@ -2,15 +2,22 @@
|
|||||||
import Sk from './skulpt/skulpt';
|
import Sk from './skulpt/skulpt';
|
||||||
import { Events, Debug } from 'mixly';
|
import { Events, Debug } from 'mixly';
|
||||||
import MIXPY_TEMPLATE from '../templates/python/mixpy.py';
|
import MIXPY_TEMPLATE from '../templates/python/mixpy.py';
|
||||||
|
import NUMPY_URL from '../others/skulpt/libs/numpy/__init__.js?url';
|
||||||
|
import PYGAL_URL from '../others/skulpt/libs/pygal/__init__.js?url';
|
||||||
|
import MATPLOTLIB_URL from '../others/skulpt/libs/matplotlib/__init__.js?url';
|
||||||
|
import PYPLOT_URL from '../others/skulpt/libs/pyplot/__init__.js?url';
|
||||||
|
import PGZHELPER_URL from '../others/skulpt/libs/pgzhelper/pgzhelper.js?url';
|
||||||
|
import SPRITE_URL from '../others/skulpt/libs/sprite/basic.js?url';
|
||||||
|
|
||||||
const externalLibs = {//外部引入的第三方库
|
|
||||||
"./numpy/__init__.js": "../common/js/skulpt_libs/numpy/__init__.js",
|
// 外部引入的第三方库
|
||||||
"./pygal/__init__.js": "../common/js/skulpt_libs/pygal/__init__.js",
|
const externalLibs = {
|
||||||
// "./numpy/random/__init__.js" : 'https://cdn.jsdelivr.net/gh/ebertmi/skulpt_numpy@master/numpy/random/__init__.js',
|
'./numpy/__init__.js': NUMPY_URL,
|
||||||
"./matplotlib/__init__.js": '../common/js/skulpt_libs/matplotlib/__init__.js',
|
'./pygal/__init__.js': PYGAL_URL,
|
||||||
"./matplotlib/pyplot/__init__.js": "../common/js/skulpt_libs/pyplot/__init__.js",
|
'./matplotlib/__init__.js': MATPLOTLIB_URL,
|
||||||
'./pgzhelper/__init__.js': "../common/js/skulpt_libs/pgzhelper/pgzhelper.js",
|
'./matplotlib/pyplot/__init__.js': PYPLOT_URL,
|
||||||
"./sprite/__init__.js": "../common/js/skulpt_libs/sprite/basic.js"
|
'./pgzhelper/__init__.js': PGZHELPER_URL,
|
||||||
|
'./sprite/__init__.js': SPRITE_URL
|
||||||
};
|
};
|
||||||
|
|
||||||
var GLOBAL_VALUE;
|
var GLOBAL_VALUE;
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
var $builtinmodule = function(name)
|
||||||
|
{
|
||||||
|
var matplotlib = {};
|
||||||
|
|
||||||
|
return matplotlib;
|
||||||
|
};
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
|||||||
|
var $builtinmodule = function (name) {
|
||||||
|
let pgzhelper= {__name__: new Sk.builtin.str("pgzhelper")};
|
||||||
|
|
||||||
|
return pgzhelper;
|
||||||
|
}
|
||||||
@@ -0,0 +1,393 @@
|
|||||||
|
var $builtinmodule = function(name) {
|
||||||
|
function fill(arr, val) {
|
||||||
|
if (Array.prototype.fill) {
|
||||||
|
return Array.prototype.fill.bind(arr)(val, arguments[2], arguments[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Steps 1-2.
|
||||||
|
if (arr == null) {
|
||||||
|
throw new TypeError('arr is null or not defined');
|
||||||
|
}
|
||||||
|
|
||||||
|
var O = Object(arr);
|
||||||
|
|
||||||
|
// Steps 3-5.
|
||||||
|
var len = O.length >>> 0;
|
||||||
|
|
||||||
|
// Steps 6-7.
|
||||||
|
var start = arguments[2];
|
||||||
|
var relativeStart = start >> 0;
|
||||||
|
|
||||||
|
// Step 8.
|
||||||
|
var k = relativeStart < 0 ?
|
||||||
|
Math.max(len + relativeStart, 0) :
|
||||||
|
Math.min(relativeStart, len);
|
||||||
|
|
||||||
|
// Steps 9-10.
|
||||||
|
var end = arguments[3];
|
||||||
|
var relativeEnd = end === undefined ?
|
||||||
|
len : end >> 0;
|
||||||
|
|
||||||
|
// Step 11.
|
||||||
|
var final = relativeEnd < 0 ?
|
||||||
|
Math.max(len + relativeEnd, 0) :
|
||||||
|
Math.min(relativeEnd, len);
|
||||||
|
|
||||||
|
// Step 12.
|
||||||
|
while (k < final) {
|
||||||
|
O[k] = value;
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 13.
|
||||||
|
return O;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var mod = {};
|
||||||
|
|
||||||
|
var COLORS = [
|
||||||
|
[255, 89, 149], [182, 227, 84], [254, 237, 108], [140, 237, 255],
|
||||||
|
[158, 111, 254], [137, 156, 161], [248, 248, 242], [191, 70, 70],
|
||||||
|
[81, 96, 131], [249, 38, 114], [130, 180, 20], [253, 151, 31],
|
||||||
|
[86, 194, 214], [128, 131, 132], [140, 84, 254], [70, 84, 87]
|
||||||
|
];
|
||||||
|
|
||||||
|
var KWARGS = ['title', 'width', 'height', 'range', 'include_x_axis', 'x_title', 'y_title', 'title_font_size', 'fill', 'stroke', 'x_labels'];
|
||||||
|
|
||||||
|
function Chart(options) {
|
||||||
|
this._options = options;
|
||||||
|
this._data = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function rgba(rgb, a) {
|
||||||
|
return 'rgba(' + rgb.join(',') + ',' + a + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
Chart.prototype.add = function(label, values) {
|
||||||
|
this._data.unshift({
|
||||||
|
name: label,
|
||||||
|
color: rgba(COLORS[this._data.length%COLORS.length], 0.75),
|
||||||
|
data: values,
|
||||||
|
marker : {
|
||||||
|
symbol: 'circle'
|
||||||
|
},
|
||||||
|
stack : 1
|
||||||
|
});
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
Chart.prototype.render = function(renderer) {
|
||||||
|
var options = this._options;
|
||||||
|
var $elem = Sk.domOutput('<div></div>');
|
||||||
|
var title_style = {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
};
|
||||||
|
if (options.title_font_size) {
|
||||||
|
title_style['font-size'] = options.title_font_size + 'px';
|
||||||
|
}
|
||||||
|
var xPlotLines = [];
|
||||||
|
var yPlotLines = [];
|
||||||
|
|
||||||
|
if (options.range) {
|
||||||
|
yPlotLines.push({
|
||||||
|
value: options.range.min,
|
||||||
|
width: 1,
|
||||||
|
color: '#FFFFFF'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultWidth = Sk.availableWidth || 400;
|
||||||
|
var defaultHeight = Math.min(defaultWidth, Sk.availableHeight || 300);
|
||||||
|
|
||||||
|
var chart = {
|
||||||
|
chart: {
|
||||||
|
width : options.width || defaultWidth,
|
||||||
|
height: options.height || defaultHeight,
|
||||||
|
backgroundColor: '#000'
|
||||||
|
},
|
||||||
|
credits: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
text: options.title,
|
||||||
|
style : title_style
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
title: {
|
||||||
|
text: options.x_title || null,
|
||||||
|
style : title_style,
|
||||||
|
margin: 20
|
||||||
|
},
|
||||||
|
categories: options.x_labels,
|
||||||
|
labels : {
|
||||||
|
enabled: options.x_labels ? true : false
|
||||||
|
},
|
||||||
|
tickLength: 0
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
startOnTick: false,
|
||||||
|
title: {
|
||||||
|
text: options.y_title || null,
|
||||||
|
style : title_style,
|
||||||
|
margin: 20
|
||||||
|
},
|
||||||
|
plotLines: yPlotLines,
|
||||||
|
min : options.include_x_axis
|
||||||
|
? 0
|
||||||
|
: options.range
|
||||||
|
? options.range.min
|
||||||
|
: null,
|
||||||
|
max : options.range ? options.range.max : null,
|
||||||
|
gridLineDashStyle : 'ShortDash',
|
||||||
|
gridLineColor: '#DDD',
|
||||||
|
tickLength: 0
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
itemStyle : {
|
||||||
|
color : '#FFFFFF'
|
||||||
|
},
|
||||||
|
layout: 'vertical',
|
||||||
|
align: 'left',
|
||||||
|
verticalAlign: 'top',
|
||||||
|
y: 50,
|
||||||
|
borderWidth: 0
|
||||||
|
},
|
||||||
|
labels : {
|
||||||
|
style : {
|
||||||
|
color: '#FFFFFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: this._data
|
||||||
|
};
|
||||||
|
|
||||||
|
for(var i = 0; i < chart.series.length; i++) {
|
||||||
|
chart.series[i].legendIndex = chart.series.length - i;
|
||||||
|
chart.series[i].index = chart.series.length - i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renderer) {
|
||||||
|
chart = renderer(options, chart);
|
||||||
|
}
|
||||||
|
|
||||||
|
$elem.highcharts(chart);
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function some(val) {
|
||||||
|
return val && val !== Sk.builtin.none.none$;
|
||||||
|
}
|
||||||
|
|
||||||
|
function kwfunc(impl, kwargs) {
|
||||||
|
if (kwargs && kwargs.length) {
|
||||||
|
impl.co_varnames = ['__self__'].concat(kwargs);
|
||||||
|
impl.$defaults = fill(new Array(kwargs.length), Sk.builtin.none.none$);
|
||||||
|
}
|
||||||
|
return new Sk.builtin.func(impl);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createChartType(type, renderer) {
|
||||||
|
mod[type] = Sk.misceval.buildClass(mod, function($gbl, $loc) {
|
||||||
|
$loc.__init__ = kwfunc(
|
||||||
|
function(self, title, width, height, range, include_x_axis, x_title, y_title, title_font_size, fill, stroke, x_labels) {
|
||||||
|
var options = {};
|
||||||
|
if (some(title)) options.title = title.v;
|
||||||
|
if (some(width)) options.width = width.v;
|
||||||
|
if (some(height)) options.height = height.v;
|
||||||
|
if (some(range)) options.range = {
|
||||||
|
min: range.v[0].v,
|
||||||
|
max: range.v[1].v
|
||||||
|
};
|
||||||
|
if (some(include_x_axis)) options.include_x_axis = include_x_axis.v;
|
||||||
|
if (some(x_title)) options.x_title = x_title.v;
|
||||||
|
if (some(y_title)) options.y_title = y_title.v;
|
||||||
|
if (some(title_font_size)) options.title_font_size = title_font_size.v;
|
||||||
|
if (some(fill)) options.fill = fill.v;
|
||||||
|
if (some(stroke)) options.stroke = stroke.v;
|
||||||
|
if (some(x_labels)) options.x_labels = x_labels.v;
|
||||||
|
|
||||||
|
self.instance = new Chart(options);
|
||||||
|
}, KWARGS
|
||||||
|
);
|
||||||
|
|
||||||
|
$loc.add = new Sk.builtin.func(function(self, label, values) {
|
||||||
|
values = (values instanceof Sk.builtin.list)
|
||||||
|
? Sk.ffi.remapToJs(values)
|
||||||
|
: [values.v];
|
||||||
|
|
||||||
|
return self.instance.add(label.v, values);
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.render = new Sk.builtin.func(function(self) {
|
||||||
|
var i, key, val;
|
||||||
|
|
||||||
|
for (i = 0; i < KWARGS.length; i++) {
|
||||||
|
key = KWARGS[i];
|
||||||
|
val = self.tp$getattr(key);
|
||||||
|
|
||||||
|
if (typeof val !== "undefined") {
|
||||||
|
self.instance._options[key] = Sk.ffi.remapToJs(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.instance.render(renderer);
|
||||||
|
});
|
||||||
|
}, type, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
createChartType('Line', function(options, chart) {
|
||||||
|
chart.chart.type = options.fill ? 'area' : 'line';
|
||||||
|
return chart;
|
||||||
|
});
|
||||||
|
createChartType('StackedLine', function(options, chart) {
|
||||||
|
chart.chart.type = options.fill ? 'area' : 'line';
|
||||||
|
chart.plotOptions = {
|
||||||
|
area : {
|
||||||
|
stacking : 'percent'
|
||||||
|
},
|
||||||
|
series : {
|
||||||
|
stacking : 'percent'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return chart;
|
||||||
|
});
|
||||||
|
createChartType('Bar', function(options, chart) {
|
||||||
|
chart.chart.type = 'column';
|
||||||
|
return chart;
|
||||||
|
});
|
||||||
|
createChartType('StackedBar', function(options, chart) {
|
||||||
|
chart.chart.type = 'column';
|
||||||
|
chart.plotOptions = {
|
||||||
|
column : {
|
||||||
|
stacking: 'percent'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return chart;
|
||||||
|
});
|
||||||
|
createChartType('HorizontalBar', function(options, chart) {
|
||||||
|
chart.chart.type = 'bar';
|
||||||
|
return chart;
|
||||||
|
});
|
||||||
|
createChartType('StackedHorizontalBar', function(options, chart) {
|
||||||
|
chart.chart.type = 'bar';
|
||||||
|
chart.plotOptions = {
|
||||||
|
bar : {
|
||||||
|
stacking: 'percent'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return chart;
|
||||||
|
});
|
||||||
|
createChartType('XY', function(options, chart) {
|
||||||
|
if (options.stroke === false) {
|
||||||
|
chart.chart.type = 'scatter'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
chart.chart.type = options.fill ? 'area' : 'line';
|
||||||
|
}
|
||||||
|
chart.xAxis.labels.enabled = true;
|
||||||
|
|
||||||
|
return chart;
|
||||||
|
});
|
||||||
|
createChartType('Radar', function(options, chart) {
|
||||||
|
chart.chart.polar = true;
|
||||||
|
chart.chart.type = 'line';
|
||||||
|
chart.xAxis = {
|
||||||
|
categories: options.x_labels,
|
||||||
|
tickmarkPlacement: 'on',
|
||||||
|
lineWidth: 0
|
||||||
|
}
|
||||||
|
chart.yAxis = {
|
||||||
|
gridLineInterpolation: 'polygon',
|
||||||
|
lineWidth: 0,
|
||||||
|
min: 0,
|
||||||
|
gridLineDashStyle : 'ShortDash',
|
||||||
|
gridLineColor: '#DDD'
|
||||||
|
}
|
||||||
|
for(var i = 0; i < chart.series.length; i++) {
|
||||||
|
chart.series[i].pointPlacement = 'on';
|
||||||
|
}
|
||||||
|
|
||||||
|
return chart;
|
||||||
|
});
|
||||||
|
createChartType('Pie', function(options, chart) {
|
||||||
|
chart.chart.type = 'pie';
|
||||||
|
var slices = [];
|
||||||
|
var breakdown = [];
|
||||||
|
var useBreakdown = false;
|
||||||
|
for(var i = 0; i < chart.series.length; i++) {
|
||||||
|
var slice = chart.series[i];
|
||||||
|
if (slice.data.length === 1) {
|
||||||
|
slices.unshift({
|
||||||
|
name : slice.name,
|
||||||
|
color : slice.color,
|
||||||
|
borderColor : slice.color,
|
||||||
|
legendIndex : slice.legendIndex,
|
||||||
|
y : slice.data[0]
|
||||||
|
});
|
||||||
|
breakdown.unshift({
|
||||||
|
name : slice.name,
|
||||||
|
color : slice.color,
|
||||||
|
borderColor : slice.color,
|
||||||
|
y : slice.data[0]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
useBreakdown = true;
|
||||||
|
var sum = 0;
|
||||||
|
var maxDecimal = 0;
|
||||||
|
for(var j = 0; j < slice.data.length; j++) {
|
||||||
|
var parts = slice.data[j].toString().split('.');
|
||||||
|
maxDecimal = Math.max(maxDecimal, parts[1] ? parts[1].length : 0);
|
||||||
|
sum += slice.data[j];
|
||||||
|
breakdown.unshift({
|
||||||
|
name: slice.name,
|
||||||
|
color: 'rgba(0,0,0,0)',
|
||||||
|
borderColor : slice.color,
|
||||||
|
y: slice.data[j]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
slices.unshift({
|
||||||
|
name : slice.name,
|
||||||
|
color : slice.color,
|
||||||
|
borderColor : slice.color,
|
||||||
|
legendIndex : slice.legendIndex,
|
||||||
|
y : parseFloat(sum.toFixed(maxDecimal))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chart.tooltip = {
|
||||||
|
formatter: function() {
|
||||||
|
return this.key + ': ' + this.y;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
chart.plotOptions = {
|
||||||
|
pie: {
|
||||||
|
allowPointSelect: !useBreakdown,
|
||||||
|
cursor: useBreakdown ? null : 'pointer',
|
||||||
|
shadow: false,
|
||||||
|
center: ['50%', '50%'],
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
chart.series = [{
|
||||||
|
name: ' ',
|
||||||
|
data: slices,
|
||||||
|
showInLegend: true
|
||||||
|
}];
|
||||||
|
if (useBreakdown) {
|
||||||
|
chart.series.push({
|
||||||
|
name: ' ',
|
||||||
|
data: breakdown,
|
||||||
|
innerSize: '90%',
|
||||||
|
showInLegend: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return chart;
|
||||||
|
});
|
||||||
|
|
||||||
|
return mod;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,229 @@
|
|||||||
|
var $builtinmodule = function (name) {
|
||||||
|
var sprite = {__name__: new Sk.builtin.str("sprite")};
|
||||||
|
sprite.createBackground = new Sk.builtin.func(function(img) {
|
||||||
|
img=Sk.ffi.remapToJs(img);
|
||||||
|
return Sk.ffi.remapToPy(SPRITE.CreateBackground(img));
|
||||||
|
});
|
||||||
|
|
||||||
|
sprite.Sprite = Sk.misceval.buildClass(sprite, function($gbl, $loc) {
|
||||||
|
|
||||||
|
$loc.__init__ = new Sk.builtin.func(function(self, img, x, y, name) {
|
||||||
|
img=Sk.ffi.remapToJs(img);
|
||||||
|
x=Sk.ffi.remapToJs(x);
|
||||||
|
y=Sk.ffi.remapToJs(y);
|
||||||
|
name=Sk.ffi.remapToJs(name);
|
||||||
|
self.v$name = Sk.ffi.remapToPy(SPRITE.CreateASprite(img, x, y, name));
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.show = new Sk.builtin.func(function(self) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
t.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.hide = new Sk.builtin.func(function(self) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
t.hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.enlarge = new Sk.builtin.func(function(self, s) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var s = Sk.ffi.remapToJs(s);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
t.enlarge(s);
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.enlargeTo = new Sk.builtin.func(function(self, s) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var s = Sk.ffi.remapToJs(s);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
t.enlargeTo(s);
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.expandTo = new Sk.builtin.func(function(self, s, time) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var s = Sk.ffi.remapToJs(s);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
return t.expandTo(s, time);
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.move = new Sk.builtin.func(function(self, step) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
step=Sk.ffi.remapToJs(step);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
t.move(step);
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.moveTo = new Sk.builtin.func(function(self, x, y) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
x=Sk.ffi.remapToJs(x);
|
||||||
|
y=Sk.ffi.remapToJs(y);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
t.moveTo(x, y);
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.slideTo = new Sk.builtin.func(function(self, x, y, time) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
x=Sk.ffi.remapToJs(x);
|
||||||
|
y=Sk.ffi.remapToJs(y);
|
||||||
|
time=Sk.ffi.remapToJs(time);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
return t.slideTo(x, y, time);
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.addX = new Sk.builtin.func(function(self, step) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
step = Sk.ffi.remapToJs(step);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
t.addX(step);
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.addY = new Sk.builtin.func(function(self, step) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
step = Sk.ffi.remapToJs(step);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
t.addY(step);
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.getX = new Sk.builtin.func(function(self) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
return Sk.ffi.remapToPy(t.x);
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.getY = new Sk.builtin.func(function(self) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
return Sk.ffi.remapToPy(t.y);
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.rotate = new Sk.builtin.func(function(self, degree) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
degree=Sk.ffi.remapToJs(degree);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
t.rotate(degree);
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.rotateTo = new Sk.builtin.func(function(self, degree) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
degree=Sk.ffi.remapToJs(degree);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
t.rotateTo(degree);
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.circleTo = new Sk.builtin.func(function(self, degree, time) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
degree=Sk.ffi.remapToJs(degree);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
return t.circleTo(degree);
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.hit = new Sk.builtin.func(function(self, sprite2) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var name2 = Sk.ffi.remapToJs(sprite2.v$name);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
var t2 = SPRITE.sprites[name2];
|
||||||
|
return Sk.ffi.remapToPy(t.hit(t2));
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.outOfScreen = new Sk.builtin.func(function(self) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
return Sk.ffi.remapToPy(t.outOfScreen());
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.mouseAction = new Sk.builtin.func(function(self, calc) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
t.mouseAction(()=>{Sk.misceval.callsim(calc)});
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.isClicked = new Sk.builtin.func(function(self) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
return Sk.ffi.remapToPy(t.isDown);
|
||||||
|
});
|
||||||
|
|
||||||
|
// new
|
||||||
|
$loc.setScale = new Sk.builtin.func(function(self, h, w) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
t.setScale(h, w);
|
||||||
|
});
|
||||||
|
$loc.filterGray = new Sk.builtin.func(function(self) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
t.filterGray();
|
||||||
|
});
|
||||||
|
$loc.filterBrighter = new Sk.builtin.func(function(self) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
t.filterBrighter();
|
||||||
|
});
|
||||||
|
$loc.filterOrigin = new Sk.builtin.func(function(self) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var t = SPRITE.sprites[name];
|
||||||
|
t.filterOrigin();
|
||||||
|
});
|
||||||
|
}, 'Sprite', []);
|
||||||
|
|
||||||
|
sprite.Text = Sk.misceval.buildClass(sprite, function($gbl, $loc) {
|
||||||
|
|
||||||
|
$loc.__init__ = new Sk.builtin.func(function(self, text, x, y, name) {
|
||||||
|
text=Sk.ffi.remapToJs(text);
|
||||||
|
x=Sk.ffi.remapToJs(x);
|
||||||
|
y=Sk.ffi.remapToJs(y);
|
||||||
|
name=Sk.ffi.remapToJs(name);
|
||||||
|
self.v$name = Sk.ffi.remapToPy(SPRITE.CreateText(text, x, y, name));
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.changeText = new Sk.builtin.func(function(self, text) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
text=Sk.ffi.remapToJs(text);
|
||||||
|
var t = SPRITE.texts[name];
|
||||||
|
t.changeText(text);
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.show = new Sk.builtin.func(function(self) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var t = SPRITE.texts[name];
|
||||||
|
t.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$loc.hide = new Sk.builtin.func(function(self) {
|
||||||
|
var name = Sk.ffi.remapToJs(self.v$name);
|
||||||
|
var t = SPRITE.texts[name];
|
||||||
|
t.hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
}, 'Text', []);
|
||||||
|
|
||||||
|
sprite.clearAllSprites = new Sk.builtin.func(function() {
|
||||||
|
return Sk.ffi.remapToPy(SPRITE.ClearAllSprites());
|
||||||
|
});
|
||||||
|
|
||||||
|
sprite.repeat = new Sk.builtin.func(function(calc = new Function()) {
|
||||||
|
SPRITE.Repeat(()=>{Sk.misceval.callsim(calc)});
|
||||||
|
});
|
||||||
|
|
||||||
|
sprite.keyboardListener = new Sk.builtin.func(function(key, calc = new Function()) {
|
||||||
|
key = Sk.ffi.remapToJs(key);
|
||||||
|
SPRITE.KeyboardListener(key,()=>{Sk.misceval.callsim(calc)});
|
||||||
|
});
|
||||||
|
|
||||||
|
sprite.isKeyboardHit = new Sk.builtin.func(function(keyvalue) {
|
||||||
|
keyvalue = Sk.ffi.remapToJs(keyvalue);
|
||||||
|
return Sk.ffi.remapToPy(SPRITE.IsKeyboardHit(keyvalue));
|
||||||
|
});
|
||||||
|
|
||||||
|
sprite.getTime = new Sk.builtin.func(function() {
|
||||||
|
return Sk.ffi.remapToPy(Math.floor(SPRITE.timer/1000));
|
||||||
|
});
|
||||||
|
|
||||||
|
sprite.clearTimer = new Sk.builtin.func(function() {
|
||||||
|
SPRITE.ClearTimer();
|
||||||
|
});
|
||||||
|
|
||||||
|
return sprite;
|
||||||
|
}
|
||||||
@@ -51,114 +51,114 @@ class StatusBarImage extends PageBase {
|
|||||||
let canvas = null;
|
let canvas = null;
|
||||||
let iframe = null;
|
let iframe = null;
|
||||||
switch (data.display_type) {
|
switch (data.display_type) {
|
||||||
case 'p5':
|
case 'p5':
|
||||||
root.style.width = '100%';
|
root.style.width = '100%';
|
||||||
root.style.height = '100%';
|
root.style.height = '100%';
|
||||||
root.style.display = 'flex';
|
root.style.display = 'flex';
|
||||||
root.style.justifyContent = 'center';
|
root.style.justifyContent = 'center';
|
||||||
root.style.alignItems = 'center';
|
root.style.alignItems = 'center';
|
||||||
|
|
||||||
// some canvas nodes can be added later so we observe...
|
// some canvas nodes can be added later so we observe...
|
||||||
new MutationObserver(function (mutationsList) {
|
new MutationObserver(function (mutationsList) {
|
||||||
mutationsList.forEach((mutation) =>
|
mutationsList.forEach((mutation) =>
|
||||||
mutation.addedNodes.forEach((node) => {
|
mutation.addedNodes.forEach((node) => {
|
||||||
const elem = node;
|
const elem = node;
|
||||||
if (
|
if (
|
||||||
elem.tagName != null &&
|
elem.tagName != null &&
|
||||||
['canvas', 'video'].includes(elem.tagName.toLowerCase())
|
['canvas', 'video'].includes(elem.tagName.toLowerCase())
|
||||||
)
|
)
|
||||||
autoFit(elem);
|
autoFit(elem);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}).observe(root, { childList: true });
|
}).observe(root, { childList: true });
|
||||||
|
|
||||||
root.querySelectorAll('canvas,video').forEach(autoFit);
|
root.querySelectorAll('canvas,video').forEach(autoFit);
|
||||||
$content.append(root);
|
$content.append(root);
|
||||||
break;
|
break;
|
||||||
case 'matplotlib':
|
case 'matplotlib':
|
||||||
canvas = root.querySelector('canvas');
|
canvas = root.querySelector('canvas');
|
||||||
if (canvas) root = canvas;
|
if (canvas) root = canvas;
|
||||||
root.style.width = '';
|
root.style.width = '';
|
||||||
root.style.height = '';
|
root.style.height = '';
|
||||||
root.style.maxWidth = '100%';
|
root.style.maxWidth = '100%';
|
||||||
root.style.maxHeight = '100%';
|
root.style.maxHeight = '100%';
|
||||||
$content.append(root);
|
$content.append(root);
|
||||||
break;
|
break;
|
||||||
case 'ocaml-canvas':
|
case 'ocaml-canvas':
|
||||||
root.style.width = '';
|
root.style.width = '';
|
||||||
root.style.height = '';
|
root.style.height = '';
|
||||||
root.style.maxWidth = '100%';
|
root.style.maxWidth = '100%';
|
||||||
root.style.maxHeight = '100%';
|
root.style.maxHeight = '100%';
|
||||||
$content.append(root);
|
$content.append(root);
|
||||||
break;
|
break;
|
||||||
case 'turtle':
|
case 'turtle':
|
||||||
// Turtle result
|
// Turtle result
|
||||||
root.setAttribute('width', '100%');
|
root.setAttribute('width', '100%');
|
||||||
root.setAttribute('height', '100%');
|
root.setAttribute('height', '100%');
|
||||||
$content.append(root.outerHTML);
|
$content.append(root.outerHTML);
|
||||||
break;
|
break;
|
||||||
case 'sympy':
|
case 'sympy':
|
||||||
$content.append(data.content);
|
$content.append(data.content);
|
||||||
if (typeof window.MathJax === 'undefined') {
|
if (typeof window.MathJax === 'undefined') {
|
||||||
// dynamically loading MathJax
|
// dynamically loading MathJax
|
||||||
console.log('Loading MathJax (Sympy expression needs it).');
|
console.log('Loading MathJax (Sympy expression needs it).');
|
||||||
(function () {
|
(function () {
|
||||||
let script = document.createElement('script');
|
let script = document.createElement('script');
|
||||||
script.type = 'text/javascript';
|
script.type = 'text/javascript';
|
||||||
script.src =
|
script.src =
|
||||||
'https://cdn.jsdelivr.net/npm/mathjax@3.0.5/es5/tex-mml-chtml.js';
|
'https://cdn.jsdelivr.net/npm/mathjax@3.0.5/es5/tex-mml-chtml.js';
|
||||||
document.getElementsByTagName('head')[0].appendChild(script);
|
document.getElementsByTagName('head')[0].appendChild(script);
|
||||||
})();
|
})();
|
||||||
} else {
|
} else {
|
||||||
// otherwise, render it
|
// otherwise, render it
|
||||||
window.MathJax.typeset();
|
window.MathJax.typeset();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'multiple':
|
case 'multiple':
|
||||||
/* typically dispached by display() */
|
/* typically dispached by display() */
|
||||||
for (let mime of [
|
for (let mime of [
|
||||||
'image/svg+xml',
|
'image/svg+xml',
|
||||||
'image/png',
|
'image/png',
|
||||||
'text/html',
|
'text/html',
|
||||||
'text/plain',
|
'text/plain',
|
||||||
]) {
|
]) {
|
||||||
if (mime in data.content) {
|
if (mime in data.content) {
|
||||||
let content = data.content[mime];
|
let content = data.content[mime];
|
||||||
if (mime === 'image/png') {
|
if (mime === 'image/png') {
|
||||||
content =
|
content =
|
||||||
'<img src="data:image/png;base64,' +
|
'<img src="data:image/png;base64,' +
|
||||||
content +
|
content +
|
||||||
'" style="max-width: 100%; max-height: 100%;">';
|
'" style="max-width: 100%; max-height: 100%;">';
|
||||||
|
}
|
||||||
|
$content.append(content);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
$content.append(content);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
break;
|
case 'tutor':
|
||||||
case 'tutor':
|
// hacky but iframe.document.body.style require to wait for
|
||||||
// hacky but iframe.document.body.style require to wait for
|
// iframe loading
|
||||||
// iframe loading
|
$content.append($(data.content.replace('overflow-y%3A%20hidden%3B', '')));
|
||||||
$content.append($(data.content.replace('overflow-y%3A%20hidden%3B', '')));
|
iframe = this.getContent()[0].getElementsByTagName('iframe')[0];
|
||||||
iframe = this.getContent()[0].getElementsByTagName('iframe')[0];
|
if (iframe == null) return;
|
||||||
if (iframe == null) return;
|
// trick to avoid taking height update into account
|
||||||
// trick to avoid taking height update into account
|
iframe.style.maxHeight = iframe.style.minHeight = '100%';
|
||||||
iframe.style.maxHeight = iframe.style.minHeight = '100%';
|
|
||||||
|
|
||||||
// force rendering when visible,
|
// force rendering when visible,
|
||||||
// otherwise, strange things happends
|
// otherwise, strange things happends
|
||||||
// since PythonTutor check for visibility at some point
|
// since PythonTutor check for visibility at some point
|
||||||
new IntersectionObserver((entries, observer) => {
|
new IntersectionObserver((entries, observer) => {
|
||||||
const entry = entries[0];
|
const entry = entries[0];
|
||||||
if (entry && !entry.isIntersecting) return;
|
if (entry && !entry.isIntersecting) return;
|
||||||
iframe.contentWindow?.postMessage({ type: 'redraw' }, '*');
|
iframe.contentWindow?.postMessage({ type: 'redraw' }, '*');
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
}).observe(iframe);
|
}).observe(iframe);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.error(
|
console.error(
|
||||||
`Not supported node type '${data.display_type}' in eval.display result processing.`
|
`Not supported node type '${data.display_type}' in eval.display result processing.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const path = require("path");
|
const path = require('path');
|
||||||
const common = require("../../../webpack.common");
|
const common = require('../../../webpack.common');
|
||||||
const { merge } = require("webpack-merge");
|
const { merge } = require('webpack-merge');
|
||||||
|
|
||||||
module.exports = merge(common, {
|
module.exports = merge(common, {
|
||||||
resolve: {
|
resolve: {
|
||||||
@@ -16,7 +16,11 @@ module.exports = merge(common, {
|
|||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.py$/,
|
test: /\.py$/,
|
||||||
type: "asset/source",
|
type: 'asset/source',
|
||||||
|
}, {
|
||||||
|
test: /\.js$/,
|
||||||
|
resourceQuery: /url/,
|
||||||
|
type: 'asset/resource',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
1
boards/default_src/python_skulpt_car/.eslintignore
Normal file
1
boards/default_src/python_skulpt_car/.eslintignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
others/skulpt
|
||||||
@@ -47,6 +47,8 @@ import * as SkulptPyTurtleGenerators from '@mixly/python-skulpt/generators/turtl
|
|||||||
|
|
||||||
import * as GameGenerators from './generators/game';
|
import * as GameGenerators from './generators/game';
|
||||||
|
|
||||||
|
import './others/loader';
|
||||||
|
|
||||||
import './css/color_mixpy_python_skulpt.css';
|
import './css/color_mixpy_python_skulpt.css';
|
||||||
|
|
||||||
Object.assign(Blockly.Variables, Variables);
|
Object.assign(Blockly.Variables, Variables);
|
||||||
|
|||||||
3
boards/default_src/python_skulpt_car/others/loader.js
Normal file
3
boards/default_src/python_skulpt_car/others/loader.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import NavExt from './nav-ext';
|
||||||
|
|
||||||
|
NavExt.init();
|
||||||
97
boards/default_src/python_skulpt_car/others/mixpy-project.js
Normal file
97
boards/default_src/python_skulpt_car/others/mixpy-project.js
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
export default class MixpyProject {
|
||||||
|
constructor() {
|
||||||
|
this.initProject();
|
||||||
|
}
|
||||||
|
|
||||||
|
initProject() {
|
||||||
|
this.fileD = {};
|
||||||
|
this.MAINF = 'main.py';
|
||||||
|
this.fileD[this.MAINF] = ["", true, 1];
|
||||||
|
this.selectFile = this.MAINF;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(file, filecontent, filetype) {
|
||||||
|
if (this.exist(file)) {
|
||||||
|
console.log("Warning:file already in project");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.fileD[file] = [filecontent, false, filetype];
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(file) {
|
||||||
|
delete this.fileD[file];
|
||||||
|
this.selectFile = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
getProject() {
|
||||||
|
return Object.keys(this.fileD);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUploadFileList() {
|
||||||
|
var fileNameList = Object.keys(this.fileD);
|
||||||
|
var ret = [];
|
||||||
|
for (var i in fileNameList) {
|
||||||
|
if (this.fileD[fileNameList[i]][2] === 2)
|
||||||
|
ret.push(fileNameList[i]);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNewFileList() {
|
||||||
|
var fileNameList = Object.keys(this.fileD);
|
||||||
|
var ret = [];
|
||||||
|
for (var i in fileNameList) {
|
||||||
|
if (this.fileD[fileNameList[i]][2] === 1)
|
||||||
|
ret.push(fileNameList[i]);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSelect(f) {
|
||||||
|
return this.fileD[f][1];
|
||||||
|
}
|
||||||
|
|
||||||
|
select(f) {
|
||||||
|
// if (this.selectFile !== undefined) {
|
||||||
|
// this.modify(this.selectFile, mixlyjs.getCodeContent());
|
||||||
|
// this.fileD[this.selectFile][1] = false;
|
||||||
|
// }
|
||||||
|
this.fileD[f][1] = true;
|
||||||
|
// this.selectFile = f;
|
||||||
|
// var suffix = mixlyjs.getFileSuffix(f);
|
||||||
|
// var textFileSuffix = ["py", "txt", "csv", "xml"];
|
||||||
|
// if (textFileSuffix.indexOf(suffix) !== -1) {
|
||||||
|
// tabClick('arduino');
|
||||||
|
// mixlyjs.renderIno(this.fileD[f][0]);
|
||||||
|
// } else {
|
||||||
|
// var base64str = 'data:image/' + suffix + ';base64,' + this.fileD[f][0];
|
||||||
|
// $('#mixpy_show_image').attr('src', base64str);
|
||||||
|
// mixlyjs.renderIno(this.fileD[f][0]);
|
||||||
|
// tabClick('image');
|
||||||
|
// var $imageA = $('#mixpy_link_image');
|
||||||
|
// $imageA.attr('href', base64str);
|
||||||
|
// $imageA.attr('download', f);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
getFileNum() {
|
||||||
|
var files = Object.keys(this.fileD);
|
||||||
|
return files.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFileContent(f) {
|
||||||
|
return this.fileD[f][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFileType(f) {
|
||||||
|
return this.fileD[f][2];
|
||||||
|
}
|
||||||
|
|
||||||
|
modify(f, content) {
|
||||||
|
this.fileD[f][0] = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
exist(f) {
|
||||||
|
return f in this.fileD;
|
||||||
|
}
|
||||||
|
}
|
||||||
42
boards/default_src/python_skulpt_car/others/nav-ext.js
Normal file
42
boards/default_src/python_skulpt_car/others/nav-ext.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { app, Nav, Debug } from 'mixly';
|
||||||
|
import * as Blockly from 'blockly/core';
|
||||||
|
import PythonShell from './python-shell';
|
||||||
|
|
||||||
|
const NavExt = {};
|
||||||
|
|
||||||
|
NavExt.init = function () {
|
||||||
|
PythonShell.init();
|
||||||
|
const nav = app.getNav();
|
||||||
|
|
||||||
|
nav.register({
|
||||||
|
icon: 'icon-play-circled',
|
||||||
|
title: '',
|
||||||
|
id: 'python-run-btn',
|
||||||
|
displayText: Blockly.Msg.MSG['run'],
|
||||||
|
preconditionFn: () => {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
callback: () => {
|
||||||
|
PythonShell.run().catch(Debug.error);
|
||||||
|
},
|
||||||
|
scopeType: Nav.Scope.LEFT,
|
||||||
|
weight: 4
|
||||||
|
});
|
||||||
|
|
||||||
|
nav.register({
|
||||||
|
icon: 'icon-cancel',
|
||||||
|
title: '',
|
||||||
|
id: 'python-stop-btn',
|
||||||
|
displayText: Blockly.Msg.MSG['stop'],
|
||||||
|
preconditionFn: () => {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
callback: () => {
|
||||||
|
PythonShell.stop().catch(Debug.error);
|
||||||
|
},
|
||||||
|
scopeType: Nav.Scope.LEFT,
|
||||||
|
weight: 5
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NavExt;
|
||||||
844
boards/default_src/python_skulpt_car/others/py-engine.js
Normal file
844
boards/default_src/python_skulpt_car/others/py-engine.js
Normal file
@@ -0,0 +1,844 @@
|
|||||||
|
/* eslint-disable new-cap */
|
||||||
|
import Sk from './skulpt/skulpt';
|
||||||
|
import $ from 'jquery';
|
||||||
|
import PyGameZero from './skulpt/pygame-zero';
|
||||||
|
import { Events, Debug } from 'mixly';
|
||||||
|
import MIXPY_TEMPLATE from '../templates/python/mixpy.py';
|
||||||
|
import BLOCKLY_GAME_URL from '../others/skulpt/libs/bg_highlight.js?url';
|
||||||
|
import INIT_TOOL_URL from '../others/skulpt/libs/inittool.js?url';
|
||||||
|
import BG_NONEHL_URL from '../others/skulpt/libs/bg_nonehl.js?url';
|
||||||
|
|
||||||
|
|
||||||
|
// 外部引入的第三方库
|
||||||
|
const externalLibs = {
|
||||||
|
'./blocklygame/__init__.js': BLOCKLY_GAME_URL,
|
||||||
|
'./blocktool/__init__.js': INIT_TOOL_URL,
|
||||||
|
'./bg_nonehl/__init__.js': BG_NONEHL_URL
|
||||||
|
};
|
||||||
|
|
||||||
|
var GLOBAL_VALUE;
|
||||||
|
|
||||||
|
function prettyPrintError(error) {
|
||||||
|
if (typeof error === "string") {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
// A weird skulpt thing?
|
||||||
|
if (error.tp$str !== undefined) {
|
||||||
|
return error.tp$str().v;
|
||||||
|
}
|
||||||
|
return "" + error.name + ": " + error.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class PyEngine {
|
||||||
|
#events_ = new Events(['input', 'output', 'display', 'finished', 'error']);
|
||||||
|
|
||||||
|
constructor(programStatus, mixpyProject) {
|
||||||
|
this.programStatus = programStatus;
|
||||||
|
this.mixpyProject = mixpyProject;
|
||||||
|
/**
|
||||||
|
* Definable function to be run when execution has fully ended,
|
||||||
|
* whether it succeeds or fails.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
this.onExecutionEnd = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEvents() {
|
||||||
|
return this.#events_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function that will attempt to call the defined onExecutionEnd,
|
||||||
|
* but will do nothing if there is no function defined.
|
||||||
|
*/
|
||||||
|
executionEnd_() {
|
||||||
|
if (this.onExecutionEnd !== null) {
|
||||||
|
this.onExecutionEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the Python Execution engine and the Printer (console).
|
||||||
|
*/
|
||||||
|
loadEngine(container) {
|
||||||
|
Sk.__future__ = Sk.python3;
|
||||||
|
// No connected services
|
||||||
|
Sk.connectedServices = {};
|
||||||
|
// No time limit
|
||||||
|
//Sk.execLimit = null;
|
||||||
|
// Ensure version 3, so we get proper print handling
|
||||||
|
// Sk.python3 = true;
|
||||||
|
//输出海龟画图图片
|
||||||
|
//如果需要输出pygame_zero场景,需要重新设置
|
||||||
|
Sk.TurtleGraphics = {
|
||||||
|
target: container,
|
||||||
|
width: 500,
|
||||||
|
height: 500
|
||||||
|
};
|
||||||
|
|
||||||
|
PyGameZero.setContainer(container);
|
||||||
|
|
||||||
|
//数据分析显示图片
|
||||||
|
Sk.MatPlotLibGraphics = {
|
||||||
|
target: container,
|
||||||
|
width: 500,
|
||||||
|
height: 500
|
||||||
|
};
|
||||||
|
// TODO
|
||||||
|
//Sk.console = printer.getConfiguration();
|
||||||
|
// Definitely use a prompt
|
||||||
|
Sk.inputfunTakesPrompt = true;
|
||||||
|
|
||||||
|
// Keeps track of the tracing while the program is executing; destroyed afterwards.
|
||||||
|
this.executionBuffer = {};
|
||||||
|
|
||||||
|
Sk.domOutput = (html) => {
|
||||||
|
const dom = this.#events_.on('display', html)[0];
|
||||||
|
return dom;
|
||||||
|
};
|
||||||
|
|
||||||
|
Sk.configure({
|
||||||
|
//设置文本输出
|
||||||
|
output: (lineText) => {
|
||||||
|
this.#events_.run('output', {
|
||||||
|
content: lineText
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Function to handle loading in new files
|
||||||
|
read: this.readFile.bind(this),
|
||||||
|
inputfun: this.skInput.bind(this),
|
||||||
|
inputfunTakesPrompt: true,
|
||||||
|
execLimit: Number.POSITIVE_INFINITY,
|
||||||
|
fileread: this.fileread.bind(this),
|
||||||
|
filewrite: this.filewrite.bind(this),
|
||||||
|
__future__: Sk.python3, //python3已成默认值,要使用python2需要单独设置
|
||||||
|
});
|
||||||
|
|
||||||
|
Sk.builtins.value = new Sk.builtin.func(function () {
|
||||||
|
return Sk.ffi.remapToPy(GLOBAL_VALUE === undefined ? 5 : GLOBAL_VALUE);
|
||||||
|
});
|
||||||
|
Sk.builtins.set_value = new Sk.builtin.func(function (v) {
|
||||||
|
GLOBAL_VALUE = v.v;
|
||||||
|
});
|
||||||
|
|
||||||
|
Sk.builtinFiles.files['./mixpy.py'] = MIXPY_TEMPLATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to access Skulpt built-ins. This is pretty generic, taken
|
||||||
|
* almost directly from the Skulpt docs.
|
||||||
|
*
|
||||||
|
* @param {String} filename - The python filename (e.g., "os" or "pprint") that will be loaded.
|
||||||
|
* @returns {String} The JavaScript source code of the file (weird, right?)
|
||||||
|
* @throws Will throw an error if the file isn't found.
|
||||||
|
*/
|
||||||
|
readFile(file) {
|
||||||
|
// console.log("Attempting file: " + Sk.ffi.remapToJs(file));
|
||||||
|
// 加载模块
|
||||||
|
if (PyGameZero.matchModelName(file)) {
|
||||||
|
return PyGameZero.load(file);
|
||||||
|
}
|
||||||
|
if (externalLibs[file] !== undefined) {
|
||||||
|
return Sk.misceval.promiseToSuspension(fetch(externalLibs[file]).then((resp) => resp.text()));
|
||||||
|
}
|
||||||
|
if (Sk.builtinFiles === undefined || Sk.builtinFiles.files[file] === undefined) {
|
||||||
|
throw "File not found: '" + file + "'";
|
||||||
|
}
|
||||||
|
return Sk.builtinFiles.files[file];
|
||||||
|
}
|
||||||
|
|
||||||
|
fileread(filename, mode) {
|
||||||
|
if (this.mixpyProject.exist(filename)) {
|
||||||
|
return this.mixpyProject.getFileContent(filename);
|
||||||
|
}
|
||||||
|
if (mode.indexOf('w') !== -1) {
|
||||||
|
this.mixpyProject.add(filename, '', 1);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
filewrite(fileItem, str) {
|
||||||
|
var filename = fileItem.name;
|
||||||
|
this.mixpyProject.modify(filename, str);
|
||||||
|
this.mixpyProject.select(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
skInput(prompt) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.#events_.run('input', {
|
||||||
|
content: {
|
||||||
|
prompt
|
||||||
|
},
|
||||||
|
resolve, reject
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the state of the execution engine, including reinitailizing
|
||||||
|
* the execution buffer (trace, step, etc.), reseting the printer, and
|
||||||
|
* hiding the trace button.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
reset() {
|
||||||
|
Sk.execLimit = Number.POSITIVE_INFINITY;
|
||||||
|
Sk.TurtleGraphics.reset && Sk.TurtleGraphics.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
kill() {
|
||||||
|
// 新增了sprite相关内容
|
||||||
|
// window.SPRITE.kill();
|
||||||
|
//点击取消按钮发送数据
|
||||||
|
Sk.execLimit = 0;
|
||||||
|
this.executionEnd_();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Steps" the execution of the code, meant to be used as a callback to the Skulpt
|
||||||
|
* environment.
|
||||||
|
*
|
||||||
|
* @param {Object} variables - Hash that maps the names of variables (Strings) to their Skulpt representation.
|
||||||
|
* @param {Number} lineNumber - The corresponding line number in the source code that is being executed.
|
||||||
|
* @param {Number} columnNumber - The corresponding column number in the source code that is being executed. Think of it as the "X" position to the lineNumber's "Y" position.
|
||||||
|
* @param {String} filename - The name of the python file being executed (e.g., "__main__.py").
|
||||||
|
* @param {String} astType - Unused? TODO: What is this?
|
||||||
|
* @param {String} ast - String-encoded JSON representation of the AST node associated with this element.
|
||||||
|
*/
|
||||||
|
step(variables, lineNumber,
|
||||||
|
columnNumber, filename) {
|
||||||
|
if (filename == '<stdin>.py') {
|
||||||
|
var currentStep = this.executionBuffer.step;
|
||||||
|
var globals = this.parseGlobals(variables);
|
||||||
|
this.executionBuffer.trace.push(
|
||||||
|
{
|
||||||
|
'step': currentStep,
|
||||||
|
'filename': filename,
|
||||||
|
//'block': highlightMap[lineNumber-1],
|
||||||
|
'line': lineNumber,
|
||||||
|
'column': columnNumber,
|
||||||
|
'properties': globals.properties,
|
||||||
|
'modules': globals.modules
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.executionBuffer.step = currentStep + 1;
|
||||||
|
this.executionBuffer.last_step = currentStep + 1;
|
||||||
|
this.executionBuffer.line_number = lineNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the AbstractInterpreter to get some static information about the code,
|
||||||
|
* in particular the variables' types. This is needed for type checking.
|
||||||
|
*
|
||||||
|
* @returns {Object<String, AIType>} Maps variable names (as Strings) to types as constructed by the AbstractIntepreter.
|
||||||
|
*/
|
||||||
|
analyzeVariables() {
|
||||||
|
// Get the code
|
||||||
|
var code = this.main.model.programs['__main__']();
|
||||||
|
if (code.trim() == "") {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// var analyzer = new AbstractInterpreter(code);
|
||||||
|
// report = analyzer.report;
|
||||||
|
// return analyzer.variableTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the AbstractInterpreter to get some static information about the code,
|
||||||
|
* including potential semantic errors. It then parses that information to give
|
||||||
|
* feedback.
|
||||||
|
*
|
||||||
|
* @returns {Boolean} Whether the code was successfully analyzed.
|
||||||
|
*/
|
||||||
|
analyze() {
|
||||||
|
this.main.model.execution.status("analyzing");
|
||||||
|
|
||||||
|
// var feedback = this.main.components.feedback;
|
||||||
|
|
||||||
|
// Get the code
|
||||||
|
var code = this.main.model.programs['__main__']();
|
||||||
|
if (code.trim() == "") {
|
||||||
|
this.main.components.feedback.emptyProgram("You haven't written any code yet!");
|
||||||
|
//this.main.model.feedback.status("semantic");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the given python code, resetting the console and Trace Table.
|
||||||
|
*/
|
||||||
|
run(code) {
|
||||||
|
// Reset everything
|
||||||
|
this.reset();
|
||||||
|
if (code.indexOf('import blocklygame') !== -1
|
||||||
|
|| code.indexOf('from blocklygame import') !== -1) {
|
||||||
|
PyGameZero.reset();
|
||||||
|
$(Sk.TurtleGraphics.target).empty();
|
||||||
|
}
|
||||||
|
if ((code.indexOf("import blocktool") !== -1) || (code.indexOf("import blocklygame") !== -1) || (code.indexOf("from blocklygame import") !== -1)) {
|
||||||
|
//正则匹配替换block id元素
|
||||||
|
var code_piece = [];
|
||||||
|
code_piece = code.split("\n");
|
||||||
|
for (var i = 0; i < code_piece.length; i++) {
|
||||||
|
if (code_piece[i].indexOf("block_id") >= 0) {
|
||||||
|
var target = "";
|
||||||
|
var re = /,?'block_id=[\s\S]*'/.exec(code_piece[i]);
|
||||||
|
if (re != null) {
|
||||||
|
target = re[0];
|
||||||
|
code_piece[i] = code_piece[i].replace(target, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//检查是否是高亮辅助块\toll,如果是,则将此行代码移除
|
||||||
|
if ((code_piece[i].indexOf("import blocktool") >= 0) || (code_piece[i].indexOf("blocktool.highlight") >= 0)) {
|
||||||
|
code_piece[i] = "delete";
|
||||||
|
}
|
||||||
|
//如果使用的是分步调试的blocklygame,要将其换成非分步调试的模块bg_nonehl
|
||||||
|
if ((code_piece[i].indexOf("blocklygame") >= 0)) {
|
||||||
|
code_piece[i] = code_piece[i].replace("blocklygame", "bg_nonehl");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code = ""
|
||||||
|
for (var i = 0; i < code_piece.length; i++) {
|
||||||
|
if (code_piece[i] != "delete") {
|
||||||
|
code += code_piece[i] + '\n'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.programStatus['running'] = true;
|
||||||
|
Sk.misceval.asyncToPromise(() => Sk.importMainWithBody("<stdin>", false, code, true))
|
||||||
|
.then(() => {
|
||||||
|
// window.SPRITE.running = false;
|
||||||
|
this.programStatus['running'] = false;
|
||||||
|
this.#events_.run('finished');
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
Debug.error(error);
|
||||||
|
// window.SPRITE.running = false;
|
||||||
|
this.programStatus['running'] = false;
|
||||||
|
this.#events_.run('error', error);
|
||||||
|
var original = prettyPrintError(error);
|
||||||
|
this.#events_.run('finished');
|
||||||
|
//hack for kill program with time limiterror
|
||||||
|
if (original.indexOf("TimeLimitError") !== -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.executionEnd_();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupEnvironment(student_code, traceTable, output, ast, final_values) {
|
||||||
|
var model = this.main.model;
|
||||||
|
this._backup_execution = Sk.afterSingleExecution;
|
||||||
|
Sk.afterSingleExecution = undefined;
|
||||||
|
Sk.builtins.get_output = new Sk.builtin.func(function () {
|
||||||
|
Sk.builtin.pyCheckArgs("get_output", arguments, 0, 0);
|
||||||
|
return Sk.ffi.remapToPy(model.execution.output());
|
||||||
|
});
|
||||||
|
Sk.builtins.reset_output = new Sk.builtin.func(function () {
|
||||||
|
Sk.builtin.pyCheckArgs("reset_output", arguments, 0, 0);
|
||||||
|
model.execution.output.removeAll();
|
||||||
|
});
|
||||||
|
Sk.builtins.log = new Sk.builtin.func(function (data) {
|
||||||
|
Sk.builtin.pyCheckArgs("log", arguments, 1, 1);
|
||||||
|
console.log(data);
|
||||||
|
});
|
||||||
|
//Sk.builtins.trace = Sk.ffi.remapToPy(traceTable);
|
||||||
|
Sk.builtins._trace = traceTable;
|
||||||
|
Sk.builtins._final_values = final_values;
|
||||||
|
Sk.builtins.code = Sk.ffi.remapToPy(student_code);
|
||||||
|
Sk.builtins.set_success = this.instructor_module.set_success;
|
||||||
|
Sk.builtins.set_feedback = this.instructor_module.set_feedback;
|
||||||
|
Sk.builtins.set_finished = this.instructor_module.set_finished;
|
||||||
|
Sk.builtins.count_components = this.instructor_module.count_components;
|
||||||
|
Sk.builtins.no_nonlist_nums = this.instructor_module.no_nonlist_nums;
|
||||||
|
Sk.builtins.only_printing_properties = this.instructor_module.only_printing_properties;
|
||||||
|
Sk.builtins.calls_function = this.instructor_module.calls_function;
|
||||||
|
Sk.builtins.get_property = this.instructor_module.get_property;
|
||||||
|
Sk.builtins.get_value_by_name = this.instructor_module.get_value_by_name;
|
||||||
|
Sk.builtins.get_value_by_type = this.instructor_module.get_value_by_type;
|
||||||
|
Sk.builtins.parse_json = this.instructor_module.parse_json;
|
||||||
|
Sk.skip_drawing = true;
|
||||||
|
model.settings.mute_printer(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
disposeEnvironment() {
|
||||||
|
Sk.afterSingleExecution = this._backup_execution;
|
||||||
|
Sk.builtins.get_output = undefined;
|
||||||
|
Sk.builtins.reset_output = undefined;
|
||||||
|
Sk.builtins.log = undefined;
|
||||||
|
Sk.builtins._trace = undefined;
|
||||||
|
Sk.builtins.trace = undefined;
|
||||||
|
Sk.builtins.code = undefined;
|
||||||
|
Sk.builtins.set_success = undefined;
|
||||||
|
Sk.builtins.set_feedback = undefined;
|
||||||
|
Sk.builtins.set_finished = undefined;
|
||||||
|
Sk.builtins.count_components = undefined;
|
||||||
|
Sk.builtins.calls_function = undefined;
|
||||||
|
Sk.builtins.get_property = undefined;
|
||||||
|
Sk.builtins.get_value_by_name = undefined;
|
||||||
|
Sk.builtins.get_value_by_type = undefined;
|
||||||
|
Sk.builtins.no_nonlist_nums = undefined;
|
||||||
|
Sk.builtins.only_printing_properties = undefined;
|
||||||
|
Sk.builtins.parse_json = undefined;
|
||||||
|
Sk.skip_drawing = false;
|
||||||
|
GLOBAL_VALUE = undefined;
|
||||||
|
this.main.model.settings.mute_printer(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
parseGlobals(variables) {
|
||||||
|
var result = Array();
|
||||||
|
var modules = Array();
|
||||||
|
for (var property in variables) {
|
||||||
|
var value = variables[property];
|
||||||
|
if (property !== "__name__" && property !== "__doc__") {
|
||||||
|
property = property.replace('_$rw$', '')
|
||||||
|
.replace('_$rn$', '');
|
||||||
|
var parsed = this.parseValue(property, value);
|
||||||
|
if (parsed !== null) {
|
||||||
|
result.push(parsed);
|
||||||
|
} else if (value.constructor == Sk.builtin.module) {
|
||||||
|
modules.push(value.$d.__name__.v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { "properties": result, "modules": modules };
|
||||||
|
}
|
||||||
|
|
||||||
|
parseValue(property, value) {
|
||||||
|
if (value == undefined) {
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': 'Unknown',
|
||||||
|
"value": 'Undefined'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
switch (value.constructor) {
|
||||||
|
case Sk.builtin.func:
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': "Function",
|
||||||
|
"value": (value.func_code.co_varnames !== undefined ?
|
||||||
|
" Arguments: " + value.func_code.co_varnames.join(", ") :
|
||||||
|
' No arguments')
|
||||||
|
};
|
||||||
|
case Sk.builtin.module: return null;
|
||||||
|
case Sk.builtin.str:
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': "String",
|
||||||
|
"value": value.$r().v
|
||||||
|
};
|
||||||
|
case Sk.builtin.none:
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': "None",
|
||||||
|
"value": "None"
|
||||||
|
};
|
||||||
|
case Sk.builtin.bool:
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': "Boolean",
|
||||||
|
"value": value.$r().v
|
||||||
|
};
|
||||||
|
case Sk.builtin.nmber:
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': "int" == value.skType ? "Integer" : "Float",
|
||||||
|
"value": value.$r().v
|
||||||
|
};
|
||||||
|
case Sk.builtin.int_:
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': "Integer",
|
||||||
|
"value": value.$r().v
|
||||||
|
};
|
||||||
|
case Sk.builtin.float_:
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': "Float",
|
||||||
|
"value": value.$r().v
|
||||||
|
};
|
||||||
|
case Sk.builtin.tuple:
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': "Tuple",
|
||||||
|
"value": value.$r().v
|
||||||
|
};
|
||||||
|
case Sk.builtin.list:
|
||||||
|
if (value.v.length <= 20) {
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': "List",
|
||||||
|
"value": value.$r().v,
|
||||||
|
'exact_value': value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': "List",
|
||||||
|
"value": "[... " + value.v.length + " elements ...]",
|
||||||
|
"exact_value": value
|
||||||
|
};
|
||||||
|
|
||||||
|
case Sk.builtin.dict:
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': "Dictionary",
|
||||||
|
"value": value.$r().v
|
||||||
|
};
|
||||||
|
case Number:
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': value % 1 === 0 ? "Integer" : "Float",
|
||||||
|
"value": value
|
||||||
|
};
|
||||||
|
case String:
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': "String",
|
||||||
|
"value": value
|
||||||
|
};
|
||||||
|
case Boolean:
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': "Boolean",
|
||||||
|
"value": (value ? "True" : "False")
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
'name': property,
|
||||||
|
'type': value.tp$name == undefined ? value : value.tp$name,
|
||||||
|
"value": value.$r == undefined ? value : value.$r().v
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skulpt Module for holding the Instructor API.
|
||||||
|
*
|
||||||
|
* This module is a little hackish. We need to sit down and reevaluate the best way to
|
||||||
|
* organize it and whether this particular structure is ideal. I suspect it should be
|
||||||
|
* it's own proper JS file.
|
||||||
|
*
|
||||||
|
* @param {String} name - The name of the module (should always be 'instructor')
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
// var instructor_module = function (name) {
|
||||||
|
// // Main module object that gets returned at the end.
|
||||||
|
// var mod = {};
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Skulpt Exception that represents a Feedback object, to be rendered to the user
|
||||||
|
// * when the feedback system finds a problem.
|
||||||
|
// *
|
||||||
|
// * @param {Array} args - A list of optional arguments to pass to the Exception.
|
||||||
|
// * Usually this will include a message for the user.
|
||||||
|
// */
|
||||||
|
// Sk.builtin.Feedback = function (args) {
|
||||||
|
// var o;
|
||||||
|
// if (!(this instanceof Sk.builtin.Feedback)) {
|
||||||
|
// o = Object.create(Sk.builtin.Feedback.prototype);
|
||||||
|
// o.constructor.apply(o, arguments);
|
||||||
|
// return o;
|
||||||
|
// }
|
||||||
|
// Sk.builtin.Exception.apply(this, arguments);
|
||||||
|
// };
|
||||||
|
// Sk.abstr.setUpInheritance("Feedback", Sk.builtin.Feedback, Sk.builtin.Exception);
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Skulpt Exception that represents a Success object, to be thrown when the user
|
||||||
|
// * completes their program successfully.
|
||||||
|
// *
|
||||||
|
// ** @param {Array} args - A list of optional arguments to pass to the Exception.
|
||||||
|
// * Usually this will be empty.
|
||||||
|
// */
|
||||||
|
// Sk.builtin.Success = function (args) {
|
||||||
|
// var o;
|
||||||
|
// if (!(this instanceof Sk.builtin.Success)) {
|
||||||
|
// o = Object.create(Sk.builtin.Success.prototype);
|
||||||
|
// o.constructor.apply(o, arguments);
|
||||||
|
// return o;
|
||||||
|
// }
|
||||||
|
// Sk.builtin.Exception.apply(this, arguments);
|
||||||
|
// };
|
||||||
|
// Sk.abstr.setUpInheritance("Success", Sk.builtin.Success, Sk.builtin.Exception);
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Skulpt Exception that represents a Finished object, to be thrown when the user
|
||||||
|
// * completes their program successfully, but isn't in a problem with a "solution".
|
||||||
|
// * This is useful for open-ended canvases where we still want to capture the students'
|
||||||
|
// * code in Canvas.
|
||||||
|
// *
|
||||||
|
// ** @param {Array} args - A list of optional arguments to pass to the Exception.
|
||||||
|
// * Usually this will be empty.
|
||||||
|
// */
|
||||||
|
// Sk.builtin.Finished = function (args) {
|
||||||
|
// var o;
|
||||||
|
// if (!(this instanceof Sk.builtin.Finished)) {
|
||||||
|
// o = Object.create(Sk.builtin.Finished.prototype);
|
||||||
|
// o.constructor.apply(o, arguments);
|
||||||
|
// return o;
|
||||||
|
// }
|
||||||
|
// Sk.builtin.Exception.apply(this, arguments);
|
||||||
|
// };
|
||||||
|
// Sk.abstr.setUpInheritance("Finished", Sk.builtin.Finished, Sk.builtin.Exception);
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * A Skulpt function that throws a Feedback exception, allowing us to give feedback
|
||||||
|
// * to the user through the Feedback panel. This function call is done for aesthetic
|
||||||
|
// * reasons, so that we are calling a function instead of raising an error. Still,
|
||||||
|
// * exceptions allow us to break out of the control flow immediately, like a
|
||||||
|
// * return, so they are a good mechanism to use under the hood.
|
||||||
|
// *
|
||||||
|
// * @param {String} message - The message to display to the user.
|
||||||
|
// */
|
||||||
|
// mod.set_feedback = new Sk.builtin.func(function (message) {
|
||||||
|
// Sk.builtin.pyCheckArgs("set_feedback", arguments, 1, 1);
|
||||||
|
// Sk.builtin.pyCheckType("message", "string", Sk.builtin.checkString(message));
|
||||||
|
// throw new Sk.builtin.Feedback(message.v);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * A Skulpt function that throws a Success exception. This will terminate the
|
||||||
|
// * feedback analysis, but reports that the students' code was successful.
|
||||||
|
// * This function call is done for aesthetic reasons, so that we are calling a
|
||||||
|
// * function instead of raising an error. Still, exceptions allow us to break
|
||||||
|
// * out of the control flow immediately, like a return would, so they are a
|
||||||
|
// * good mechanism to use under the hood.
|
||||||
|
// */
|
||||||
|
// mod.set_success = new Sk.builtin.func(function () {
|
||||||
|
// Sk.builtin.pyCheckArgs("set_success", arguments, 0, 0);
|
||||||
|
// throw new Sk.builtin.Success();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * A Skulpt function that throws a Finished exception. This will terminate the
|
||||||
|
// * feedback analysis, but reports that the students' code was successful.
|
||||||
|
// * This function call is done for aesthetic reasons, so that we are calling a
|
||||||
|
// * function instead of raising an error. Still, exceptions allow us to break
|
||||||
|
// * out of the control flow immediately, like a return would, so they are a
|
||||||
|
// * good mechanism to use under the hood.
|
||||||
|
// */
|
||||||
|
// mod.set_finished = new Sk.builtin.func(function () {
|
||||||
|
// Sk.builtin.pyCheckArgs("set_finished", arguments, 0, 0);
|
||||||
|
// throw new Sk.builtin.Finished();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // Memoization of previous parses - some mild redundancy to save time
|
||||||
|
// // TODO: There's no evidence this is good, and could be a memory hog on big
|
||||||
|
// // programs. Someone should investigate this. The assumption is that multiple
|
||||||
|
// // helper functions might be using parses. But shouldn't we trim old parses?
|
||||||
|
// // Perhaps a timed cache would work better.
|
||||||
|
// var parses = {};
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Given source code as a string, return a flat list of all of the AST elements
|
||||||
|
// * in the parsed source code.
|
||||||
|
// *
|
||||||
|
// * TODO: There's redundancy here, since the source code was previously parsed
|
||||||
|
// * to run the file and to execute it. We should probably be able to do this just
|
||||||
|
// * once and shave off time.
|
||||||
|
// *
|
||||||
|
// * @param {String} source - Python source code.
|
||||||
|
// * @returns {Array.<Object>}
|
||||||
|
// */
|
||||||
|
// function getParseList(source) {
|
||||||
|
// if (!(source in parses)) {
|
||||||
|
// var parse = Sk.parse("__main__", source);
|
||||||
|
// parses[source] = Sk.astFromParse(parse.cst, "__main__", parse.flags);
|
||||||
|
// }
|
||||||
|
// var ast = parses[source];
|
||||||
|
// return (new NodeVisitor()).recursive_walk(ast);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Given source code as a string, return a list of all of the AST elements
|
||||||
|
// * that are Num (aka numeric literals) but that are not inside List elements.
|
||||||
|
// *
|
||||||
|
// * @param {String} source - Python source code.
|
||||||
|
// * @returns {Array.number} The list of JavaScript numeric literals that were found.
|
||||||
|
// */
|
||||||
|
// function getNonListNums(source) {
|
||||||
|
// if (!(source in parses)) {
|
||||||
|
// var parse = Sk.parse("__main__", source);
|
||||||
|
// parses[source] = Sk.astFromParse(parse.cst, "__main__", parse.flags);
|
||||||
|
// }
|
||||||
|
// var ast = parses[source];
|
||||||
|
// var visitor = new NodeVisitor();
|
||||||
|
// var insideList = false;
|
||||||
|
// var nums = [];
|
||||||
|
// visitor.visit_List = function (node) {
|
||||||
|
// insideList = true;
|
||||||
|
// this.generic_visit(node);
|
||||||
|
// insideList = false;
|
||||||
|
// }
|
||||||
|
// visitor.visit_Num = function (node) {
|
||||||
|
// if (!insideList) {
|
||||||
|
// nums.push(node.n);
|
||||||
|
// }
|
||||||
|
// this.generic_visit(node);
|
||||||
|
// }
|
||||||
|
// visitor.visit(ast);
|
||||||
|
// return nums;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Given source code as a string, return a list of all of the AST elements
|
||||||
|
// * that are being printed (using the print function) but are not variables.
|
||||||
|
// *
|
||||||
|
// * @param {String} source - Python source code.
|
||||||
|
// * @returns {Array.<Object>} The list of AST elements that were found.
|
||||||
|
// */
|
||||||
|
// function getPrintedNonProperties(source) {
|
||||||
|
// if (!(source in parses)) {
|
||||||
|
// var parse = Sk.parse("__main__", source);
|
||||||
|
// parses[source] = Sk.astFromParse(parse.cst, "__main__", parse.flags);
|
||||||
|
// }
|
||||||
|
// var ast = parses[source];
|
||||||
|
// var visitor = new NodeVisitor();
|
||||||
|
// var nonVariables = [];
|
||||||
|
// visitor.visit_Call = function (node) {
|
||||||
|
// var func = node.func;
|
||||||
|
// var args = node.args;
|
||||||
|
// if (func._astname == 'Name' && func.id.v == 'print') {
|
||||||
|
// for (var i = 0; i < args.length; i += 1) {
|
||||||
|
// if (args[i]._astname != "Name") {
|
||||||
|
// nonVariables.push(args[i]);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// this.generic_visit(node);
|
||||||
|
// }
|
||||||
|
// visitor.visit(ast);
|
||||||
|
// return nonVariables;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Skulpt function to iterate through the final state of
|
||||||
|
// * all the variables in the program, and check to see if they have
|
||||||
|
// * a given value.
|
||||||
|
// */
|
||||||
|
// mod.get_value_by_name = new Sk.builtin.func(function (name) {
|
||||||
|
// Sk.builtin.pyCheckArgs("get_value_by_name", arguments, 1, 1);
|
||||||
|
// Sk.builtin.pyCheckType("name", "string", Sk.builtin.checkString(name));
|
||||||
|
// name = name.v;
|
||||||
|
// var final_values = Sk.builtins._final_values;
|
||||||
|
// if (name in final_values) {
|
||||||
|
// return final_values[name];
|
||||||
|
// }
|
||||||
|
// return Sk.builtin.none.none$;
|
||||||
|
|
||||||
|
// });
|
||||||
|
// mod.get_value_by_type = new Sk.builtin.func(function (type) {
|
||||||
|
// Sk.builtin.pyCheckArgs("get_value_by_type", arguments, 1, 1);
|
||||||
|
|
||||||
|
// var final_values = Sk.builtins._final_values;
|
||||||
|
// var result = [];
|
||||||
|
// for (var property in final_values) {
|
||||||
|
// if (final_values[property].tp$name == type.tp$name) {
|
||||||
|
// result.push(final_values[property]);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return Sk.builtin.list(result);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// mod.parse_json = new Sk.builtin.func(function (blob) {
|
||||||
|
// Sk.builtin.pyCheckArgs("parse_json", arguments, 1, 1);
|
||||||
|
// Sk.builtin.pyCheckType("blob", "string", Sk.builtin.checkString(blob));
|
||||||
|
// blob = blob.v;
|
||||||
|
// return Sk.ffi.remapToPy(JSON.parse(blob));
|
||||||
|
// });
|
||||||
|
// mod.get_property = new Sk.builtin.func(function (name) {
|
||||||
|
// Sk.builtin.pyCheckArgs("get_property", arguments, 1, 1);
|
||||||
|
// Sk.builtin.pyCheckType("name", "string", Sk.builtin.checkString(name));
|
||||||
|
// name = name.v;
|
||||||
|
// var trace = Sk.builtins._trace;
|
||||||
|
// if (trace.length <= 0) {
|
||||||
|
// return Sk.builtin.none.none$;
|
||||||
|
// }
|
||||||
|
// var properties = trace[trace.length - 1]["properties"];
|
||||||
|
// for (var i = 0, len = properties.length; i < len; i += 1) {
|
||||||
|
// if (properties[i]['name'] == name) {
|
||||||
|
// return Sk.ffi.remapToPy(properties[i])
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return Sk.builtin.none.none$;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// mod.calls_function = new Sk.builtin.func(function (source, name) {
|
||||||
|
// Sk.builtin.pyCheckArgs("calls_function", arguments, 2, 2);
|
||||||
|
// Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
|
||||||
|
// Sk.builtin.pyCheckType("name", "string", Sk.builtin.checkString(name));
|
||||||
|
|
||||||
|
// source = source.v;
|
||||||
|
// name = name.v;
|
||||||
|
|
||||||
|
// var ast_list = getParseList(source);
|
||||||
|
|
||||||
|
// var count = 0;
|
||||||
|
// for (var i = 0, len = ast_list.length; i < len; i = i + 1) {
|
||||||
|
// if (ast_list[i]._astname == 'Call') {
|
||||||
|
// if (ast_list[i].func._astname == 'Attribute') {
|
||||||
|
// count += Sk.ffi.remapToJs(ast_list[i].func.attr) == name | 0;
|
||||||
|
// } else if (ast_list[i].func._astname == 'Name') {
|
||||||
|
// count += Sk.ffi.remapToJs(ast_list[i].func.id) == name | 0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return Sk.ffi.remapToPy(count > 0);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// mod.count_components = new Sk.builtin.func(function (source, component) {
|
||||||
|
// Sk.builtin.pyCheckArgs("count_components", arguments, 2, 2);
|
||||||
|
// Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
|
||||||
|
// Sk.builtin.pyCheckType("component", "string", Sk.builtin.checkString(component));
|
||||||
|
|
||||||
|
// source = source.v;
|
||||||
|
// component = component.v;
|
||||||
|
|
||||||
|
// var ast_list = getParseList(source);
|
||||||
|
|
||||||
|
// var count = 0;
|
||||||
|
// for (var i = 0, len = ast_list.length; i < len; i = i + 1) {
|
||||||
|
// if (ast_list[i]._astname == component) {
|
||||||
|
// count = count + 1;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return Sk.ffi.remapToPy(count);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// mod.no_nonlist_nums = new Sk.builtin.func(function (source) {
|
||||||
|
// Sk.builtin.pyCheckArgs("no_nonlist_nums", arguments, 1, 1);
|
||||||
|
// Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
|
||||||
|
|
||||||
|
// source = source.v;
|
||||||
|
|
||||||
|
// var num_list = getNonListNums(source);
|
||||||
|
|
||||||
|
// var count = 0;
|
||||||
|
// for (var i = 0, len = num_list.length; i < len; i = i + 1) {
|
||||||
|
// if (num_list[i].v != 0 && num_list[i].v != 1) {
|
||||||
|
// return Sk.ffi.remapToPy(true);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return Sk.ffi.remapToPy(false);
|
||||||
|
// });
|
||||||
|
// mod.only_printing_properties = new Sk.builtin.func(function (source) {
|
||||||
|
// Sk.builtin.pyCheckArgs("only_printing_properties", arguments, 1, 1);
|
||||||
|
// Sk.builtin.pyCheckType("source", "string", Sk.builtin.checkString(source));
|
||||||
|
|
||||||
|
// source = source.v;
|
||||||
|
|
||||||
|
// var non_var_list = getPrintedNonProperties(source);
|
||||||
|
// return Sk.ffi.remapToPy(non_var_list.length == 0);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// return mod;
|
||||||
|
// }
|
||||||
189
boards/default_src/python_skulpt_car/others/python-shell.js
Normal file
189
boards/default_src/python_skulpt_car/others/python-shell.js
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
import MixpyProject from './mixpy-project';
|
||||||
|
import PyEngine from './py-engine';
|
||||||
|
import { Workspace, Msg } from 'mixly';
|
||||||
|
import StatusBarImage from './statusbar-image';
|
||||||
|
|
||||||
|
class PythonShell {
|
||||||
|
static {
|
||||||
|
this.pythonShell = null;
|
||||||
|
|
||||||
|
this.init = async function () {
|
||||||
|
StatusBarImage.init();
|
||||||
|
this.pythonShell = new PythonShell();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.run = function () {
|
||||||
|
const mainWorkspace = Workspace.getMain();
|
||||||
|
const editor = mainWorkspace.getEditorsManager().getActive();
|
||||||
|
const code = editor.getCode();
|
||||||
|
return this.pythonShell.run(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stop = function () {
|
||||||
|
return this.pythonShell.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#statusBarTerminal_ = null;
|
||||||
|
#statusBarImage_ = null;
|
||||||
|
#statusBarsManager_ = null;
|
||||||
|
#cursor_ = {
|
||||||
|
row: 0,
|
||||||
|
column: 0
|
||||||
|
};
|
||||||
|
#prompt_ = '';
|
||||||
|
#inputResolve_ = null;
|
||||||
|
#inputReject_ = null;
|
||||||
|
#waittingForInput_ = false;
|
||||||
|
#running_ = false;
|
||||||
|
#pyEngine_ = null;
|
||||||
|
#onCursorChangeEvent_ = () => this.#onCursorChange_();
|
||||||
|
#commands_ = [
|
||||||
|
{
|
||||||
|
name: 'REPL-Enter',
|
||||||
|
bindKey: 'Enter',
|
||||||
|
exec: (editor) => {
|
||||||
|
const session = editor.getSession();
|
||||||
|
const cursor = session.selection.getCursor();
|
||||||
|
if (cursor.row === this.#cursor_.row) {
|
||||||
|
const newPos = this.#statusBarTerminal_.getEndPos();
|
||||||
|
let str = this.#statusBarTerminal_.getValueRange(this.#cursor_, newPos);
|
||||||
|
str = str.replace(this.#prompt_, '');
|
||||||
|
this.#inputResolve_?.(str);
|
||||||
|
this.#inputResolve_ = null;
|
||||||
|
this.#inputReject_ = null;
|
||||||
|
this.#statusBarTerminal_.addValue('\n');
|
||||||
|
this.#exitInput_();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
name: 'REPL-ChangeEditor',
|
||||||
|
bindKey: 'Delete|Ctrl-X|Backspace',
|
||||||
|
exec: (editor) => {
|
||||||
|
const session = editor.getSession();
|
||||||
|
const cursor = session.selection.getCursor();
|
||||||
|
if (cursor.row < this.#cursor_.row || cursor.column <= this.#cursor_.column) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
constructor() {
|
||||||
|
const mainWorkspace = Workspace.getMain();
|
||||||
|
this.#statusBarsManager_ = mainWorkspace.getStatusBarsManager();
|
||||||
|
this.#statusBarTerminal_ = this.#statusBarsManager_.getStatusBarById('output');
|
||||||
|
this.#statusBarImage_ = this.#statusBarsManager_.getStatusBarById('images');
|
||||||
|
this.#pyEngine_ = new PyEngine({}, new MixpyProject());
|
||||||
|
console.log(this.#statusBarImage_.getContent().children())
|
||||||
|
this.#pyEngine_.loadEngine(this.#statusBarImage_.getContent().children()[0]);
|
||||||
|
this.#addEventsListener_();
|
||||||
|
}
|
||||||
|
|
||||||
|
#addEventsListener_() {
|
||||||
|
const events = this.#pyEngine_.getEvents();
|
||||||
|
events.bind('finished', () => {
|
||||||
|
this.#running_ = false;
|
||||||
|
this.#statusBarTerminal_.addValue(`\n==${Msg.Lang['shell.finish']}==`);
|
||||||
|
});
|
||||||
|
|
||||||
|
events.bind('output', (data) => {
|
||||||
|
this.#statusBarTerminal_.addValue(data.content);
|
||||||
|
});
|
||||||
|
|
||||||
|
events.bind('error', (data) => {
|
||||||
|
this.#running_ = false;
|
||||||
|
this.#statusBarTerminal_.addValue(`\n${data.toString()}\n`);
|
||||||
|
});
|
||||||
|
|
||||||
|
events.bind('input', (data) => {
|
||||||
|
const prompt = String(data?.content?.prompt);
|
||||||
|
this.#statusBarTerminal_.addValue(`>>> ${prompt}`);
|
||||||
|
this.#prompt_ = prompt;
|
||||||
|
this.#inputResolve_ = data.resolve;
|
||||||
|
this.#inputReject_ = data.reject;
|
||||||
|
this.#enterInput_();
|
||||||
|
});
|
||||||
|
|
||||||
|
events.bind('display', (data) => {
|
||||||
|
this.#statusBarsManager_.changeTo('images');
|
||||||
|
this.#statusBarImage_.display(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#onCursorChange_() {
|
||||||
|
const editor = this.#statusBarTerminal_.getEditor();
|
||||||
|
const session = editor.getSession();
|
||||||
|
const cursor = session.selection.getCursor();
|
||||||
|
editor.setReadOnly(
|
||||||
|
cursor.row < this.#cursor_.row || cursor.column < this.#cursor_.column
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#enterInput_() {
|
||||||
|
if (!this.#running_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#waittingForInput_ = true;
|
||||||
|
this.#cursor_ = this.#statusBarTerminal_.getEndPos();
|
||||||
|
const editor = this.#statusBarTerminal_.getEditor();
|
||||||
|
editor.setReadOnly(false);
|
||||||
|
editor.focus();
|
||||||
|
const session = editor.getSession();
|
||||||
|
session.selection.on('changeCursor', this.#onCursorChangeEvent_);
|
||||||
|
editor.commands.addCommands(this.#commands_);
|
||||||
|
}
|
||||||
|
|
||||||
|
#exitInput_() {
|
||||||
|
this.#waittingForInput_ = false;
|
||||||
|
const editor = this.#statusBarTerminal_.getEditor();
|
||||||
|
const session = editor.getSession();
|
||||||
|
session.selection.off('changeCursor', this.#onCursorChangeEvent_);
|
||||||
|
editor.commands.removeCommands(this.#commands_);
|
||||||
|
this.#prompt_ = '';
|
||||||
|
this.#inputResolve_?.('');
|
||||||
|
// this.#inputReject_?.({});
|
||||||
|
this.cursor_ = { row: 0, column: 0 };
|
||||||
|
editor.setReadOnly(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
addPrompt(prompt, resolve, reject) {
|
||||||
|
this.#statusBarTerminal_.addValue(prompt);
|
||||||
|
this.#prompt_ = prompt;
|
||||||
|
this.#inputResolve_ = resolve;
|
||||||
|
this.#inputReject_ = reject;
|
||||||
|
this.#enterInput_();
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(code) {
|
||||||
|
await this.stop();
|
||||||
|
this.#statusBarsManager_.changeTo('output');
|
||||||
|
this.#statusBarsManager_.show();
|
||||||
|
this.#statusBarTerminal_.setValue(`${Msg.Lang['shell.running']}...\n`);
|
||||||
|
this.#running_ = true;
|
||||||
|
if (code.indexOf('import blocklygame') !== -1
|
||||||
|
|| code.indexOf('from blocklygame import') !== -1) {
|
||||||
|
this.#statusBarsManager_.changeTo('images');
|
||||||
|
}
|
||||||
|
this.#pyEngine_.run(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
async stop() {
|
||||||
|
if (this.#waittingForInput_) {
|
||||||
|
this.#exitInput_();
|
||||||
|
}
|
||||||
|
if (this.#running_) {
|
||||||
|
this.#pyEngine_.kill();
|
||||||
|
await this.sleep(500);
|
||||||
|
this.#running_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PythonShell;
|
||||||
311
boards/default_src/python_skulpt_car/others/skulpt/debugger.js
Normal file
311
boards/default_src/python_skulpt_car/others/skulpt/debugger.js
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
/**
|
||||||
|
* Debugger support for skulpt module
|
||||||
|
*/
|
||||||
|
import './skulpt.min.js';
|
||||||
|
|
||||||
|
function hasOwnProperty(obj, prop) {
|
||||||
|
var proto = obj.constructor.prototype;
|
||||||
|
return (prop in obj) &&
|
||||||
|
(!(prop in proto) || proto[prop] !== obj[prop]);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Breakpoint {
|
||||||
|
constructor(filename, lineno, colno) {
|
||||||
|
this.filename = filename;
|
||||||
|
this.lineno = lineno;
|
||||||
|
this.colno = colno;
|
||||||
|
this.enabled = true;
|
||||||
|
this.ignore_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Debugger {
|
||||||
|
constructor(filename, output_callback) {
|
||||||
|
this.dbg_breakpoints = {};
|
||||||
|
this.tmp_breakpoints = {};
|
||||||
|
this.suspension_stack = [];
|
||||||
|
this.current_suspension = -1;
|
||||||
|
this.eval_callback = null;
|
||||||
|
this.suspension = null;
|
||||||
|
this.output_callback = output_callback;
|
||||||
|
this.step_mode = false;
|
||||||
|
this.filename = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
print(txt) {
|
||||||
|
if (this.output_callback != null) {
|
||||||
|
this.output_callback.print(txt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_source_line(lineno) {
|
||||||
|
if (this.output_callback != null) {
|
||||||
|
return this.output_callback.get_source_line(lineno);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
move_up_the_stack() {
|
||||||
|
this.current_suspension = Math.min(this.current_suspension + 1, this.suspension_stack.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
move_down_the_stack() {
|
||||||
|
this.current_suspension = Math.max(this.current_suspension - 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
enable_step_mode() {
|
||||||
|
this.step_mode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
disable_step_mode() {
|
||||||
|
this.step_mode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_suspension_stack() {
|
||||||
|
return this.suspension_stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_active_suspension() {
|
||||||
|
if (this.suspension_stack.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.suspension_stack[this.current_suspension];
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_breakpoint_key(filename, lineno) {
|
||||||
|
var key = filename + "-" + lineno;
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
check_breakpoints(filename, lineno, colno) {
|
||||||
|
// If Step mode is enabled then ignore breakpoints since we will just break
|
||||||
|
// at every line.
|
||||||
|
if (this.step_mode === true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = this.generate_breakpoint_key(filename, lineno, colno);
|
||||||
|
if (hasOwnProperty(this.dbg_breakpoints, key) &&
|
||||||
|
this.dbg_breakpoints[key].enabled === true) {
|
||||||
|
var bp = null;
|
||||||
|
if (hasOwnProperty(this.tmp_breakpoints, key)) {
|
||||||
|
delete this.dbg_breakpoints[key];
|
||||||
|
delete this.tmp_breakpoints[key];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dbg_breakpoints[key].ignore_count -= 1;
|
||||||
|
this.dbg_breakpoints[key].ignore_count = Math.max(0, this.dbg_breakpoints[key].ignore_count);
|
||||||
|
|
||||||
|
bp = this.dbg_breakpoints[key];
|
||||||
|
if (bp.ignore_count === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_breakpoints_list() {
|
||||||
|
return this.dbg_breakpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
disable_breakpoint(filename, lineno, colno) {
|
||||||
|
var key = this.generate_breakpoint_key(filename, lineno, colno);
|
||||||
|
|
||||||
|
if (hasOwnProperty(this.dbg_breakpoints, key)) {
|
||||||
|
this.dbg_breakpoints[key].enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enable_breakpoint(filename, lineno, colno) {
|
||||||
|
var key = this.generate_breakpoint_key(filename, lineno, colno);
|
||||||
|
|
||||||
|
if (hasOwnProperty(this.dbg_breakpoints, key)) {
|
||||||
|
this.dbg_breakpoints[key].enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_breakpoint(filename, lineno, colno) {
|
||||||
|
var key = this.generate_breakpoint_key(filename, lineno, colno);
|
||||||
|
if (hasOwnProperty(this.dbg_breakpoints, key)) {
|
||||||
|
delete this.dbg_breakpoints[key];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return "Invalid breakpoint specified: " + filename + " line: " + lineno;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_all_breakpoints() {
|
||||||
|
this.dbg_breakpoints = {};
|
||||||
|
this.tmp_breakpoints = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
set_ignore_count(filename, lineno, colno, count) {
|
||||||
|
var key = this.generate_breakpoint_key(filename, lineno, colno);
|
||||||
|
if (hasOwnProperty(this.dbg_breakpoints, key)) {
|
||||||
|
var bp = this.dbg_breakpoints[key];
|
||||||
|
bp.ignore_count = count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_condition(filename, lineno, colno, lhs, cond, rhs) {
|
||||||
|
var key = this.generate_breakpoint_key(filename, lineno, colno);
|
||||||
|
var bp;
|
||||||
|
if (hasOwnProperty(this.dbg_breakpoints, key)) {
|
||||||
|
// Set a new condition
|
||||||
|
bp = this.dbg_breakpoints[key];
|
||||||
|
} else {
|
||||||
|
bp = new Breakpoint(filename, lineno, colno);
|
||||||
|
}
|
||||||
|
|
||||||
|
bp.condition = new window.Sk.Condition(lhs, cond, rhs);
|
||||||
|
this.dbg_breakpoints[key] = bp;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_suspension_info(suspension) {
|
||||||
|
var filename = suspension.filename;
|
||||||
|
var lineno = suspension.lineno;
|
||||||
|
var colno = suspension.colno;
|
||||||
|
this.print("Hit Breakpoint at <" + filename + "> at line: " + lineno + " column: " + colno + "\n");
|
||||||
|
this.print("----------------------------------------------------------------------------------\n");
|
||||||
|
this.print(" ==> " + this.get_source_line(lineno - 1) + "\n");
|
||||||
|
this.print("----------------------------------------------------------------------------------\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
set_suspension(suspension) {
|
||||||
|
var parent = null;
|
||||||
|
if (!hasOwnProperty(suspension, "filename") && suspension.child instanceof window.Sk.misceval.Suspension) {
|
||||||
|
suspension = suspension.child;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop the last suspension of the stack if there is more than 0
|
||||||
|
if (this.suspension_stack.length > 0) {
|
||||||
|
this.suspension_stack.pop();
|
||||||
|
this.current_suspension -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unroll the stack to get each suspension.
|
||||||
|
while (suspension instanceof window.Sk.misceval.Suspension) {
|
||||||
|
parent = suspension;
|
||||||
|
this.suspension_stack.push(parent);
|
||||||
|
this.current_suspension += 1;
|
||||||
|
suspension = suspension.child;
|
||||||
|
}
|
||||||
|
|
||||||
|
suspension = parent;
|
||||||
|
|
||||||
|
this.print_suspension_info(suspension);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_breakpoint(filename, lineno, colno, temporary) {
|
||||||
|
var key = this.generate_breakpoint_key(filename, lineno, colno);
|
||||||
|
this.dbg_breakpoints[key] = new Breakpoint(filename, lineno, colno);
|
||||||
|
if (temporary) {
|
||||||
|
this.tmp_breakpoints[key] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspension_handler(susp) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
try {
|
||||||
|
resolve(susp.resume());
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
resume() {
|
||||||
|
// Reset the suspension stack to the topmost
|
||||||
|
this.current_suspension = this.suspension_stack.length - 1;
|
||||||
|
|
||||||
|
if (this.suspension_stack.length === 0) {
|
||||||
|
this.print("No running program");
|
||||||
|
} else {
|
||||||
|
var promise = this.suspension_handler(this.get_active_suspension());
|
||||||
|
promise.then(this.success.bind(this), this.error.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pop_suspension_stack() {
|
||||||
|
this.suspension_stack.pop();
|
||||||
|
this.current_suspension -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
success(r) {
|
||||||
|
if (r instanceof window.Sk.misceval.Suspension) {
|
||||||
|
this.set_suspension(r);
|
||||||
|
} else {
|
||||||
|
if (this.suspension_stack.length > 0) {
|
||||||
|
// Current suspension needs to be popped of the stack
|
||||||
|
this.pop_suspension_stack();
|
||||||
|
|
||||||
|
if (this.suspension_stack.length === 0) {
|
||||||
|
this.print("Program execution complete");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parent_suspension = this.get_active_suspension();
|
||||||
|
// The child has completed the execution. So override the child's resume
|
||||||
|
// so we can continue the execution.
|
||||||
|
parent_suspension.child.resume = function () {
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
this.resume();
|
||||||
|
} else {
|
||||||
|
this.print("Program execution complete");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error(e) {
|
||||||
|
this.print("Traceback (most recent call last):");
|
||||||
|
for (var idx = 0; idx < e.traceback.length; ++idx) {
|
||||||
|
this.print(" File \"" + e.traceback[idx].filename + "\", line " + e.traceback[idx].lineno + ", in <module>");
|
||||||
|
var code = this.get_source_line(e.traceback[idx].lineno - 1);
|
||||||
|
code = code.trim();
|
||||||
|
code = " " + code;
|
||||||
|
this.print(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
var err_ty = e.constructor.tp$name;
|
||||||
|
for (idx = 0; idx < e.args.v.length; ++idx) {
|
||||||
|
this.print(err_ty + ": " + e.args.v[idx].v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
asyncToPromise(suspendablefn, suspHandlers, debugger_obj) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
try {
|
||||||
|
var r = suspendablefn();
|
||||||
|
|
||||||
|
(function handleResponse(r) {
|
||||||
|
try {
|
||||||
|
while (r instanceof window.Sk.misceval.Suspension) {
|
||||||
|
debugger_obj.set_suspension(r);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(r);
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
})(r);
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
execute(suspendablefn) {
|
||||||
|
var r = suspendablefn();
|
||||||
|
|
||||||
|
if (r instanceof window.Sk.misceval.Suspension) {
|
||||||
|
this.suspensions.concat(r);
|
||||||
|
this.eval_callback(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
1205
boards/default_src/python_skulpt_car/others/skulpt/libs/bg_nonehl.js
Normal file
1205
boards/default_src/python_skulpt_car/others/skulpt/libs/bg_nonehl.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
|||||||
|
var $builtinmodule = function (name) {
|
||||||
|
let mod= {__name__: new Sk.builtin.str("blocktool")};
|
||||||
|
|
||||||
|
var highlight = function(id) {
|
||||||
|
id=Sk.ffi.remapToJs(id)
|
||||||
|
Mixly.Editor.blockEditor.highlightBlock(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
var highlight_f=function(block_id) {
|
||||||
|
return new Sk.misceval.promiseToSuspension(new Promise(function(resolve) {
|
||||||
|
setTimeout( () => {
|
||||||
|
highlight(block_id)
|
||||||
|
resolve(Sk.builtin.none.none$);
|
||||||
|
}, 800);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
mod.highlight = new Sk.builtin.func(highlight_f);
|
||||||
|
|
||||||
|
return mod;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import './pygame-zero.min.js';
|
||||||
|
|
||||||
|
export default window.PyGameZero;
|
||||||
15269
boards/default_src/python_skulpt_car/others/skulpt/pygame-zero.min.js
vendored
Normal file
15269
boards/default_src/python_skulpt_car/others/skulpt/pygame-zero.min.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
|
|||||||
|
import './skulpt.min.js';
|
||||||
|
import './skulpt-stdlib.js';
|
||||||
|
|
||||||
|
export default window.Sk;
|
||||||
1058
boards/default_src/python_skulpt_car/others/skulpt/skulpt.min.js
vendored
Normal file
1058
boards/default_src/python_skulpt_car/others/skulpt/skulpt.min.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
166
boards/default_src/python_skulpt_car/others/statusbar-image.js
Normal file
166
boards/default_src/python_skulpt_car/others/statusbar-image.js
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
import STATUS_BAR_IMAGE_TEMPLATE from '../templates/html/statusbar-image.html';
|
||||||
|
import {
|
||||||
|
PageBase,
|
||||||
|
HTMLTemplate,
|
||||||
|
StatusBarsManager,
|
||||||
|
Workspace
|
||||||
|
} from 'mixly';
|
||||||
|
import $ from 'jquery';
|
||||||
|
|
||||||
|
class StatusBarImage extends PageBase {
|
||||||
|
static {
|
||||||
|
HTMLTemplate.add(
|
||||||
|
'html/statusbar/statusbar-image.html',
|
||||||
|
new HTMLTemplate(STATUS_BAR_IMAGE_TEMPLATE)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.init = function () {
|
||||||
|
StatusBarsManager.typesRegistry.register(['images'], StatusBarImage);
|
||||||
|
const mainWorkspace = Workspace.getMain();
|
||||||
|
const statusBarsManager = mainWorkspace.getStatusBarsManager();
|
||||||
|
statusBarsManager.add('images', 'images', '图像');
|
||||||
|
statusBarsManager.changeTo('output');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
const $content = $(HTMLTemplate.get('html/statusbar/statusbar-image.html').render());
|
||||||
|
this.setContent($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
super.init();
|
||||||
|
this.hideCloseBtn();
|
||||||
|
}
|
||||||
|
|
||||||
|
clean() {
|
||||||
|
this.getContent().empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
display(data) {
|
||||||
|
const $content = this.getContent();
|
||||||
|
const autoFit = function (node) {
|
||||||
|
node.style.width = 'auto';
|
||||||
|
node.style.height = 'auto';
|
||||||
|
node.style.maxWidth = '100%';
|
||||||
|
node.style.maxHeight = '100%';
|
||||||
|
};
|
||||||
|
this.clean();
|
||||||
|
let root = data.content;
|
||||||
|
let canvas = null;
|
||||||
|
let iframe = null;
|
||||||
|
switch (data.display_type) {
|
||||||
|
case 'p5':
|
||||||
|
root.style.width = '100%';
|
||||||
|
root.style.height = '100%';
|
||||||
|
root.style.display = 'flex';
|
||||||
|
root.style.justifyContent = 'center';
|
||||||
|
root.style.alignItems = 'center';
|
||||||
|
|
||||||
|
// some canvas nodes can be added later so we observe...
|
||||||
|
new MutationObserver(function (mutationsList) {
|
||||||
|
mutationsList.forEach((mutation) =>
|
||||||
|
mutation.addedNodes.forEach((node) => {
|
||||||
|
const elem = node;
|
||||||
|
if (
|
||||||
|
elem.tagName != null &&
|
||||||
|
['canvas', 'video'].includes(elem.tagName.toLowerCase())
|
||||||
|
)
|
||||||
|
autoFit(elem);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}).observe(root, { childList: true });
|
||||||
|
|
||||||
|
root.querySelectorAll('canvas,video').forEach(autoFit);
|
||||||
|
$content.append(root);
|
||||||
|
break;
|
||||||
|
case 'matplotlib':
|
||||||
|
canvas = root.querySelector('canvas');
|
||||||
|
if (canvas) root = canvas;
|
||||||
|
root.style.width = '';
|
||||||
|
root.style.height = '';
|
||||||
|
root.style.maxWidth = '100%';
|
||||||
|
root.style.maxHeight = '100%';
|
||||||
|
$content.append(root);
|
||||||
|
break;
|
||||||
|
case 'ocaml-canvas':
|
||||||
|
root.style.width = '';
|
||||||
|
root.style.height = '';
|
||||||
|
root.style.maxWidth = '100%';
|
||||||
|
root.style.maxHeight = '100%';
|
||||||
|
$content.append(root);
|
||||||
|
break;
|
||||||
|
case 'turtle':
|
||||||
|
// Turtle result
|
||||||
|
root.setAttribute('width', '100%');
|
||||||
|
root.setAttribute('height', '100%');
|
||||||
|
$content.append(root.outerHTML);
|
||||||
|
break;
|
||||||
|
case 'sympy':
|
||||||
|
$content.append(data.content);
|
||||||
|
if (typeof window.MathJax === 'undefined') {
|
||||||
|
// dynamically loading MathJax
|
||||||
|
console.log('Loading MathJax (Sympy expression needs it).');
|
||||||
|
(function () {
|
||||||
|
let script = document.createElement('script');
|
||||||
|
script.type = 'text/javascript';
|
||||||
|
script.src =
|
||||||
|
'https://cdn.jsdelivr.net/npm/mathjax@3.0.5/es5/tex-mml-chtml.js';
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(script);
|
||||||
|
})();
|
||||||
|
} else {
|
||||||
|
// otherwise, render it
|
||||||
|
window.MathJax.typeset();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'multiple':
|
||||||
|
/* typically dispached by display() */
|
||||||
|
for (let mime of [
|
||||||
|
'image/svg+xml',
|
||||||
|
'image/png',
|
||||||
|
'text/html',
|
||||||
|
'text/plain',
|
||||||
|
]) {
|
||||||
|
if (mime in data.content) {
|
||||||
|
let content = data.content[mime];
|
||||||
|
if (mime === 'image/png') {
|
||||||
|
content =
|
||||||
|
'<img src="data:image/png;base64,' +
|
||||||
|
content +
|
||||||
|
'" style="max-width: 100%; max-height: 100%;">';
|
||||||
|
}
|
||||||
|
$content.append(content);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'tutor':
|
||||||
|
// hacky but iframe.document.body.style require to wait for
|
||||||
|
// iframe loading
|
||||||
|
$content.append($(data.content.replace('overflow-y%3A%20hidden%3B', '')));
|
||||||
|
iframe = this.getContent()[0].getElementsByTagName('iframe')[0];
|
||||||
|
if (iframe == null) return;
|
||||||
|
// trick to avoid taking height update into account
|
||||||
|
iframe.style.maxHeight = iframe.style.minHeight = '100%';
|
||||||
|
|
||||||
|
// force rendering when visible,
|
||||||
|
// otherwise, strange things happends
|
||||||
|
// since PythonTutor check for visibility at some point
|
||||||
|
new IntersectionObserver((entries, observer) => {
|
||||||
|
const entry = entries[0];
|
||||||
|
if (entry && !entry.isIntersecting) return;
|
||||||
|
iframe.contentWindow?.postMessage({ type: 'redraw' }, '*');
|
||||||
|
observer.disconnect();
|
||||||
|
}).observe(iframe);
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(
|
||||||
|
`Not supported node type '${data.display_type}' in eval.display result processing.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StatusBarImage;
|
||||||
@@ -1,16 +1,3 @@
|
|||||||
<script src="../../../common/js/py2block_editor.js" type="application/javascript"></script>
|
|
||||||
<script src="../../../common/js/python_to_blockly.js" type="application/javascript"></script>
|
|
||||||
<script src="../../../common/js/mixpyProject.js" type="application/javascript"></script>
|
|
||||||
<!-- 使用最新版本的skulpt V1.3 -->
|
|
||||||
<!-- <script src="../../../common/js/skulpt/dist/version_1.0.0/skulpt.min.js"
|
|
||||||
type="application/javascript"></script>
|
|
||||||
<script src="../../../common/js/skulpt/dist/version_1.0.0/skulpt-stdlib.js"
|
|
||||||
type="application/javascript"></script> -->
|
|
||||||
<script src="../../../common/js/skulpt_mixcar/dist/new/skulpt.min.js" type="text/javascript"></script>
|
|
||||||
<script src="../../../common/js/skulpt_mixcar/dist/new/skulpt-stdlib.js" type="text/javascript"></script>
|
|
||||||
<script src="../../../common/js/skulpt_mixcar/engine.js" type="application/javascript"></script>
|
|
||||||
<script src="../../../common/js/skulpt_mixcar/pygame_zero_main.js" type="application/javascript"></script>
|
|
||||||
<script src="./others/pyengine.js" type="application/javascript"></script>
|
|
||||||
<%= htmlWebpackPlugin.tags.headTags.join('\n') %>
|
<%= htmlWebpackPlugin.tags.headTags.join('\n') %>
|
||||||
<xml>
|
<xml>
|
||||||
<category name="" id="catInOut" colour="20">
|
<category name="" id="catInOut" colour="20">
|
||||||
@@ -1179,7 +1166,13 @@ type="application/javascript"></script> -->
|
|||||||
|
|
||||||
<!-- 游戏:需要添加 -->
|
<!-- 游戏:需要添加 -->
|
||||||
<category id="catGame" name="game" colour="270">
|
<category id="catGame" name="game" colour="270">
|
||||||
<!-- <block type="initSettedMap_1"></block> -->
|
<block type="initSettedMap_1"></block>
|
||||||
|
<block type="initSettedMap_2"></block>
|
||||||
|
<block type="initSettedMap_3"></block>
|
||||||
|
<block type="initSettedMap_4"></block>
|
||||||
|
<block type="initSettedMap_5"></block>
|
||||||
|
<block type="initSettedMap_6"></block>
|
||||||
|
<block type="initSettedMap_7"></block>
|
||||||
<block type="move_direction_steps">
|
<block type="move_direction_steps">
|
||||||
<value name="times">
|
<value name="times">
|
||||||
<shadow type="math_number">
|
<shadow type="math_number">
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<style>
|
||||||
|
div[m-id="{{d.mId}}"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-bs-theme=light] div[m-id="{{d.mId}}"] {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-bs-theme=dark] div[m-id="{{d.mId}}"] {
|
||||||
|
background-color: #1e1e1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[m-id="{{d.mId}}"] > #output-img {
|
||||||
|
width: 400px;
|
||||||
|
height: fit-content;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div m-id="{{d.mId}}" class="page-item mixly-scrollbar">
|
||||||
|
<div id="output-img"></div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
|
def math_map(v, al, ah, bl, bh):
|
||||||
|
return bl + (bh - bl) * (v - al) / (ah - al)
|
||||||
|
|
||||||
|
def math_mean(myList):
|
||||||
|
localList = [e for e in myList if type(e) == int or type(e) == float]
|
||||||
|
if not localList: return
|
||||||
|
return float(sum(localList)) / len(localList)
|
||||||
|
|
||||||
|
def math_median(myList):
|
||||||
|
localList = sorted([e for e in myList if type(e) == int or type(e) == float])
|
||||||
|
if not localList: return
|
||||||
|
if len(localList) % 2 == 0:
|
||||||
|
return (localList[len(localList) // 2 - 1] + localList[len(localList) // 2]) / 2.0
|
||||||
|
else:
|
||||||
|
return localList[(len(localList) - 1) // 2]
|
||||||
|
|
||||||
|
def math_modes(some_list):
|
||||||
|
modes = []
|
||||||
|
# Using a lists of [item, count] to keep count rather than dict
|
||||||
|
# to avoid "unhashable" errors when the counted item is itself a list or dict.
|
||||||
|
counts = []
|
||||||
|
maxCount = 1
|
||||||
|
for item in some_list:
|
||||||
|
found = False
|
||||||
|
for count in counts:
|
||||||
|
if count[0] == item:
|
||||||
|
count[1] += 1
|
||||||
|
maxCount = max(maxCount, count[1])
|
||||||
|
found = True
|
||||||
|
if not found:
|
||||||
|
counts.append([item, 1])
|
||||||
|
for counted_item, item_count in counts:
|
||||||
|
if item_count == maxCount:
|
||||||
|
modes.append(counted_item)
|
||||||
|
return modes
|
||||||
|
|
||||||
|
def math_standard_deviation(numbers):
|
||||||
|
n = len(numbers)
|
||||||
|
if n == 0: return
|
||||||
|
mean = float(sum(numbers)) / n
|
||||||
|
variance = sum((x - mean) ** 2 for x in numbers) / n
|
||||||
|
return math.sqrt(variance)
|
||||||
|
|
||||||
|
def lists_sort(my_list, type, reverse):
|
||||||
|
def try_float(s):
|
||||||
|
try:
|
||||||
|
return float(s)
|
||||||
|
except:
|
||||||
|
return 0
|
||||||
|
key_funcs = {
|
||||||
|
"NUMERIC": try_float,
|
||||||
|
"TEXT": str,
|
||||||
|
"IGNORE_CASE": lambda s: str(s).lower()
|
||||||
|
}
|
||||||
|
key_func = key_funcs[type]
|
||||||
|
list_cpy = list(my_list)
|
||||||
|
return sorted(list_cpy, key=key_func, reverse=reverse)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
const path = require("path");
|
const path = require('path');
|
||||||
const common = require("../../../webpack.common");
|
const common = require('../../../webpack.common');
|
||||||
const { merge } = require("webpack-merge");
|
const { merge } = require('webpack-merge');
|
||||||
|
|
||||||
module.exports = merge(common, {
|
module.exports = merge(common, {
|
||||||
resolve: {
|
resolve: {
|
||||||
@@ -8,5 +8,20 @@ module.exports = merge(common, {
|
|||||||
'@mixly/python': path.resolve(__dirname, '../python'),
|
'@mixly/python': path.resolve(__dirname, '../python'),
|
||||||
'@mixly/python-skulpt': path.resolve(__dirname, '../python_skulpt')
|
'@mixly/python-skulpt': path.resolve(__dirname, '../python_skulpt')
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
externals: {
|
||||||
|
'sk': 'Sk'
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.py$/,
|
||||||
|
type: 'asset/source',
|
||||||
|
}, {
|
||||||
|
test: /\.js$/,
|
||||||
|
resourceQuery: /url/,
|
||||||
|
type: 'asset/resource',
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user