diff --git a/boards/default_src/python_skulpt_car/others/nav-ext.js b/boards/default_src/python_skulpt_car/others/nav-ext.js index 52aabb33..53800d64 100644 --- a/boards/default_src/python_skulpt_car/others/nav-ext.js +++ b/boards/default_src/python_skulpt_car/others/nav-ext.js @@ -1,13 +1,64 @@ -import { app, Nav, Debug } from 'mixly'; +import $ from 'jquery'; import * as Blockly from 'blockly/core'; +import { + app, + Nav, + Debug, + HTMLTemplate, + Msg, + Workspace, + Storage +} from 'mixly'; import PythonShell from './python-shell'; +import LEVEL_SELECTOR_TEMPLATE from '../templates/html/level-selector.html'; + const NavExt = {}; +const LEVELS = [ + ` + + `, + ` + + `, + ` + + `, + ` + + `, + ` + + `, + ` + + `, + ` + + ` +]; +NavExt.$shadow = $('
'); +NavExt.count = 0; NavExt.init = function () { PythonShell.init(); const nav = app.getNav(); + nav.register({ + icon: 'icon-play-circled', + title: '', + id: 'python-steprun-btn', + displayText: Blockly.Msg.MSG['step_run'], + preconditionFn: () => { + return true; + }, + callback: () => { + PythonShell.steprun().catch(Debug.error); + }, + scopeType: Nav.Scope.LEFT, + weight: 4 + }); + nav.register({ icon: 'icon-play-circled', title: '', @@ -20,7 +71,7 @@ NavExt.init = function () { PythonShell.run().catch(Debug.error); }, scopeType: Nav.Scope.LEFT, - weight: 4 + weight: 5 }); nav.register({ @@ -35,8 +86,41 @@ NavExt.init = function () { PythonShell.stop().catch(Debug.error); }, scopeType: Nav.Scope.LEFT, - weight: 5 + weight: 6 }); + + const template = new HTMLTemplate(LEVEL_SELECTOR_TEMPLATE); + const $selector = $(template.render()); + nav.getBoardSelector().before($selector); + $selector.select2({ + width: '90px', + minimumResultsForSearch: Infinity, + dropdownCssClass: `mixly-scrollbar mixly-${template.getId()}`, + dropdownAutoWidth: true, + placeholder: '', + language: Msg.nowLang + }); + for (let i = 0; i < LEVELS.length; i++) { + const option = new Option(`关卡 ${i + 1}`, i); + $selector.append(option); + } + $selector.on('select2:select', (event) => { + const { data } = event.params; + const mainWorkspace = Workspace.getMain(); + const editor = mainWorkspace.getEditorsManager().getActive(); + editor.setValue(LEVELS[parseInt(data.id)], '.mix'); + }); + $selector.on('select2:opening', () => { + NavExt.count += 1; + $(document.body).append(NavExt.$shadow); + }); + $selector.on('select2:closing', () => { + NavExt.count -= 1; + !NavExt.count && NavExt.$shadow.detach(); + }); + $selector.trigger('change'); + Storage.board('mix', LEVELS[0]); + Storage.board('path', ''); } export default NavExt; \ No newline at end of file diff --git a/boards/default_src/python_skulpt_car/others/py-engine.js b/boards/default_src/python_skulpt_car/others/py-engine.js index 77337b81..3e68f79c 100644 --- a/boards/default_src/python_skulpt_car/others/py-engine.js +++ b/boards/default_src/python_skulpt_car/others/py-engine.js @@ -268,6 +268,53 @@ export default class PyEngine { return true; } + /** + * Runs the given python code, resetting the console and Trace Table. + * 分步调试代码 + */ + steprun(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("settedMap(4") != -1 | code.indexOf("settedMap(6") != -1) { + if (code.indexOf("moveDirection") != -1) {//初始化的时候不加这行代码 + code = code + "actor.isCirculationRight()\n" + } + } + + //除了第六关,其他把检查是否成功代码加进去 + if (code.indexOf("settedMap(5)") == -1) { + if (code.indexOf("moveDirection") != -1) {//初始化的时候不加这行代码 + code = code + "actor.isSuccess()\n" + } + } + this.programStatus['running'] = true; + Sk.misceval.asyncToPromise(() => Sk.importMainWithBody("", 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_(); + }); + } + /** * Runs the given python code, resetting the console and Trace Table. */ @@ -308,6 +355,19 @@ export default class PyEngine { } } } + //如果是第五关、第七关,则需要把检查循环次数的代码加进去 + if (code.indexOf("settedMap(4") != -1 | code.indexOf("settedMap(6") != -1) { + if (code.indexOf("moveDirection") != -1) {//初始化的时候不加这行代码 + code = code + "actor.isCirculationRight();\n" + } + } + + //除了第六关,其他把检查是否成功代码加进去 + if (code.indexOf("settedMap(5)") == -1) { + if (code.indexOf("moveDirection") != -1) {//初始化的时候不加这行代码 + code = code + "actor.isSuccess();\n" + } + } this.programStatus['running'] = true; Sk.misceval.asyncToPromise(() => Sk.importMainWithBody("", false, code, true)) .then(() => { diff --git a/boards/default_src/python_skulpt_car/others/python-shell.js b/boards/default_src/python_skulpt_car/others/python-shell.js index 9a6c1c41..45b25764 100644 --- a/boards/default_src/python_skulpt_car/others/python-shell.js +++ b/boards/default_src/python_skulpt_car/others/python-shell.js @@ -12,6 +12,13 @@ class PythonShell { this.pythonShell = new PythonShell(); } + this.steprun = function () { + const mainWorkspace = Workspace.getMain(); + const editor = mainWorkspace.getEditorsManager().getActive(); + const code = editor.getCode(); + return this.pythonShell.steprun(code); + } + this.run = function () { const mainWorkspace = Workspace.getMain(); const editor = mainWorkspace.getEditorsManager().getActive(); @@ -77,7 +84,6 @@ class PythonShell { 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_(); } @@ -157,6 +163,19 @@ class PythonShell { this.#enterInput_(); } + async steprun(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_.steprun(code); + } + async run(code) { await this.stop(); this.#statusBarsManager_.changeTo('output'); diff --git a/boards/default_src/python_skulpt_car/others/skulpt/libs/bg_highlight.js b/boards/default_src/python_skulpt_car/others/skulpt/libs/bg_highlight.js index 19afcf81..8c458557 100644 --- a/boards/default_src/python_skulpt_car/others/skulpt/libs/bg_highlight.js +++ b/boards/default_src/python_skulpt_car/others/skulpt/libs/bg_highlight.js @@ -2,7 +2,7 @@ var $builtinmodule = function (name) { let mod= {__name__: new Sk.builtin.str("blocklygame")}; - var svg = d3.select('#blocklySVG').append('svg'); + var svg = d3.select(Sk.TurtleGraphics.target).append('svg'); //其他变量设置 var map=//迷宫布局 @@ -63,7 +63,8 @@ var $builtinmodule = function (name) { marker_num:0, oil:1,//表示小车有充足的油量(为了适应教材而新增的变量) traffic_light:22,//表示红绿灯为绿灯 - circulation_num:0//小车在赛道中循环的次数 + circulation_num:0,//小车在赛道中循环的次数 + invisible_mark:0//在地图中不可显示的标记数目,用于检测小车是否沿着特定路线走。规定不可见的标记点INVIMAKER=24 }; //迷宫变量 var maze_SQUARE_SIZE = 50; @@ -84,7 +85,8 @@ var $builtinmodule = function (name) { OPEN: 1, START: 2, FINISH: 3, - AWARD:4//金币奖励 + AWARD:4,//金币奖励 + INVIMAKER:24 }, //迷宫部分参数指定 MAZE_WIDTH : maze_SQUARE_SIZE * maze_COLS, @@ -92,7 +94,8 @@ var $builtinmodule = function (name) { PATH_WIDTH : maze_SQUARE_SIZE / 3, result : ResultType.UNSET, finish : {x:0,y:0}, - type:1//类型为用户自定义的 + type:1,//类型为用户自定义的 + INVIMNUM : 0 }; //已经设置好的关卡的map @@ -363,7 +366,7 @@ var $builtinmodule = function (name) { * @param {number=} opt_angle Optional angle (in degrees) to rotate Pegman. */ var displayPegman = function(x, y, d, opt_angle) { - var pegmanIcon = $('#pegman'); + var pegmanIcon = $(Sk.TurtleGraphics.target).find('#pegman'); if(actor.type=='animate'){ if(maze.type==0){ pegmanIcon.attr('x', x * maze_SQUARE_SIZE - d * actor.width+ 1); @@ -383,7 +386,7 @@ var $builtinmodule = function (name) { pegmanIcon.attr('x', x * maze_SQUARE_SIZE + 1); pegmanIcon.attr('y', maze_SQUARE_SIZE * (y + 0.5) - actor.height / 2 ); } - var clipRect = $('#clipRect'); + var clipRect = $(Sk.TurtleGraphics.target).find('#clipRect'); clipRect.attr('x', x * maze_SQUARE_SIZE + 1); clipRect.attr('y', pegmanIcon.attr('y')); }; @@ -391,7 +394,8 @@ var $builtinmodule = function (name) { var initPegman=function(){ // Pegman's clipPath element, whose (x, y) is reset by Maze.displayPegman svg.append('clipPath').attr('id','pegmanClipPath') - d3.select("#pegmanClipPath").append('rect').attr('id','clipRect').attr('width', actor.width).attr('height', actor.height) + const elem = Sk.TurtleGraphics.target.querySelector("#pegmanClipPath"); + d3.select(elem).append('rect').attr('id','clipRect').attr('width', actor.width).attr('height', actor.height) if(actor.type=="animate"){ if(maze.type==0){ @@ -462,13 +466,13 @@ var $builtinmodule = function (name) { var top = tile_SHAPES[tileShape][1]; // Tile's clipPath element. svg.append('clipPath').attr('id','tileClipPath' + tileId) - d3.select("#tileClipPath" + tileId).append('rect').attr('x', x * maze_SQUARE_SIZE).attr('y', y * maze_SQUARE_SIZE).attr('width', maze_SQUARE_SIZE).attr('height', maze_SQUARE_SIZE) - + const elem = Sk.TurtleGraphics.target.querySelector("#tileClipPath" + tileId); + d3.select(elem).append('rect').attr('x', x * maze_SQUARE_SIZE).attr('y', y * maze_SQUARE_SIZE).attr('width', maze_SQUARE_SIZE).attr('height', maze_SQUARE_SIZE) if(maze.type==0){//非用户自定义 // Tile sprite. if ((map[y][x] != maze.SquareType.WALL) && (map[y][x] != maze.SquareType.OIL_STATION) && (map[y][x] != maze.SquareType.TRAFFIC_LIGHT)&& (map[y][x] != maze.SquareType.LIGHT_GREEN)&& (map[y][x] != maze.SquareType.LIGHT_RED)) { svg.append('image').attr('x', x * maze_SQUARE_SIZE).attr('y', y * maze_SQUARE_SIZE).attr('width',maze_SQUARE_SIZE ).attr('height',maze_SQUARE_SIZE ) - .attr('clip-path', 'url(#tileClipPath' + tileId + ')').attr('xlink:href',maze.tiles) + .attr('clip-path', 'url(#tileClipPath' + tileId + ')').attr('xlink:href',maze.tiles); tileId++; } }else{ @@ -519,7 +523,7 @@ var $builtinmodule = function (name) { actor.y= y; } else if (map[y][x] == maze.SquareType.FINISH) { // Move the finish icon into position. - var finishIcon = $('#finish'); + var finishIcon = $(Sk.TurtleGraphics.target).find('#finish'); finishIcon.attr('x', maze_SQUARE_SIZE * (x + 0.5) - finishIcon.attr('width') / 2); finishIcon.attr('y', maze_SQUARE_SIZE * (y + 0.6) - @@ -538,7 +542,7 @@ var $builtinmodule = function (name) { svg.append('image').attr('id','finish').attr('width', maze_SQUARE_SIZE*0.8).attr('height', maze_SQUARE_SIZE*0.8).attr('xlink:href',maze.marker) actor.x= x; actor.y= y; - var finishIcon = $('#finish'); + var finishIcon = $(Sk.TurtleGraphics.target).find('#finish'); finishIcon.attr('x', maze_SQUARE_SIZE * x+5 ); finishIcon.attr('y', maze_SQUARE_SIZE * y+5); maze.finish={x:x,y:y} @@ -574,7 +578,6 @@ var $builtinmodule = function (name) { return true }else{ if(maze.mlevel==5 || maze.mlevel==7 ||maze.mlevel==6){ - console.log(11) return true }else{ if(actor.marker_num==maze_marker_num){ @@ -713,7 +716,7 @@ var $builtinmodule = function (name) { var hasCoin=function(x , y) { if(map[y][x]==maze.SquareType.AWARD){//如果此处是金币 setTimeout(function() { - $('#coin'+y+x).remove() + $(Sk.TurtleGraphics.target).find('#coin'+y+x).remove() }, actor.stepSpeed * 3) map[y][x]=maze.SquareType.OPEN actor.coin_point+=1 @@ -1147,7 +1150,8 @@ var $builtinmodule = function (name) { map[actor.y][actor.x+1]=Math.random()>0.5? maze.SquareType.LIGHT_RED:maze.SquareType.LIGHT_GREEN;//随机刷新红绿灯的状态 actor.traffic_light=map[actor.y][actor.x+1]; if(actor.traffic_light==maze.SquareType.LIGHT_RED){//图像变为红灯 - d3.select("#lightgreen").remove(); + const elem = Sk.TurtleGraphics.target.querySelector("#lightgreen"); + d3.select(elem).remove(); svg.append('image').attr('id','lightred').attr('x',(actor.x+1) * maze_SQUARE_SIZE-5).attr('y',actor.y * maze_SQUARE_SIZE+5).attr('width',maze_SQUARE_SIZE).attr('height',maze_SQUARE_SIZE) .attr('xlink:href','../common/js/skulpt_mixcar/pic/redlight.png') } @@ -1307,7 +1311,6 @@ var $builtinmodule = function (name) { Sk.builtin.pyCheckArgs("isSuccess", arguments, 1,1); return new Sk.misceval.promiseToSuspension(new Promise(function(resolve) { var state=checkFinish() - console.log(state) if(state==true){ setTimeout(function() { layer.alert("挑战成功!", { shade: false }); diff --git a/boards/default_src/python_skulpt_car/others/skulpt/libs/bg_nonehl.js b/boards/default_src/python_skulpt_car/others/skulpt/libs/bg_nonehl.js index 4e0b5d35..18296d3f 100644 --- a/boards/default_src/python_skulpt_car/others/skulpt/libs/bg_nonehl.js +++ b/boards/default_src/python_skulpt_car/others/skulpt/libs/bg_nonehl.js @@ -579,7 +579,6 @@ var $builtinmodule = function (name) { return true }else{ if(maze.mlevel==5 || maze.mlevel==7 ||maze.mlevel==6){ - console.log(11) return true }else{ if(actor.marker_num==maze_marker_num){ @@ -1172,7 +1171,6 @@ var $builtinmodule = function (name) { Sk.builtin.pyCheckArgs("isSuccess", arguments, 1,1); return new Sk.misceval.promiseToSuspension(new Promise(function(resolve) { var state=checkFinish() - console.log(state) if(state==true){ setTimeout(function() { layer.alert("挑战成功!", { shade: false }); diff --git a/boards/default_src/python_skulpt_car/template.xml b/boards/default_src/python_skulpt_car/template.xml index b441fb93..ef5730bd 100644 --- a/boards/default_src/python_skulpt_car/template.xml +++ b/boards/default_src/python_skulpt_car/template.xml @@ -1166,13 +1166,6 @@ - - - - - - - diff --git a/boards/default_src/python_skulpt_car/templates/html/level-selector.html b/boards/default_src/python_skulpt_car/templates/html/level-selector.html new file mode 100644 index 00000000..df1ebd85 --- /dev/null +++ b/boards/default_src/python_skulpt_car/templates/html/level-selector.html @@ -0,0 +1,13 @@ + + \ No newline at end of file