Update: 添加新板卡 Python 3 Online

This commit is contained in:
王立帮
2024-09-08 13:32:13 +08:00
parent f1d75df7b1
commit 2435f5cdfa
51 changed files with 4345 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
node_modules
build
origin

View File

@@ -0,0 +1,168 @@
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(1) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/inout.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(1) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/inout2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(2) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/ctrl.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(2) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/ctrl2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(3) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/math.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(3) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/math2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(4) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/logic.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(4) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/logic2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(5) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/text.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(5) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/text2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(6) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/list3.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(6) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/list4.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(7) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/tuple.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(7) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/tuple2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(8) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/dict.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(8) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/dict2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(9) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/set.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(9) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/set2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(10) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/var.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(10) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/var2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(11) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/func.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(11) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/func2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(12) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/file.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(12) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/file2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(13) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/requests.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(13) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/requests2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(14) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/mixio.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(14) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/mixio2.png') no-repeat;
background-size: 100% auto;
}
#catMixIO.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/mixio.png') no-repeat;
background-size: 100% auto;
}
#catMixIO.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/mixio2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(15) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/turtle.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(15) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/turtle2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(16) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/ai.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(16) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/ai2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(17) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/data.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(17) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/data2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(18) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/cv.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(18) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/cv2.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(19) > div.blocklyTreeRow > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/algorithm.png') no-repeat;
background-size: 100% auto;
}
div.blocklyToolboxDiv > div.blocklyToolboxContents > div:nth-child(19) > div.blocklyTreeRow.blocklyTreeSelected > div.blocklyTreeRowContentContainer > span.blocklyTreeIcon{
background:url('../../../../common/media/mark/algorithm2.png') no-repeat;
background-size: 100% auto;
}

View File

@@ -0,0 +1,46 @@
import { Python } from '@mixly/python';
import KEYBOARD_INTERRUPT_TEMPLATE from './templates/python/keyboard-interrupt.py';
Python.finish = function (code) {
// Convert the definitions dictionary into a list.
if (code !== "") {
code = code.replace(/\n/g, '\n');
code = code.replace(/\n\s+$/, '\n');
}
var definitions = [];
for (var name in Python.definitions_) {
definitions.push(Python.definitions_[name]);
}
var functions = [];
for (let name in Python.functions_) {
functions.push(Python.functions_[name]);
}
var setups = [];
for (let name in Python.setups_) {
setups.push(Python.setups_[name]);
}
if (setups.length !== 0)
setups.push('\n');
var loops = [];
for (let name in Python.loops_) {
loops.push(Python.loops_[name]);
}
var codeEnd = [];
for (let name in Python.codeEnd_) {
codeEnd.push(Python.codeEnd_[name]);
}
if (codeEnd.length !== 0)
codeEnd.push('\n');
// Clean up temporary data.
//delete Python.definitions_;
//delete Python.functionNames_;
//Python.variableDB_.reset();
if (loops.length > 0)
return KEYBOARD_INTERRUPT_TEMPLATE + definitions.join('\n') + '\n' + functions.join('\n')
+ '\n' + setups.join('') + '\n' + code
+ 'while True:\n' + loops.join('') + codeEnd.join('\n');
return KEYBOARD_INTERRUPT_TEMPLATE + definitions.join('\n') + '\n' + functions.join('\n') + '\n'
+ setups.join('') + '\n' + code + codeEnd.join('\n');
}
export default Python;

View File

@@ -0,0 +1,143 @@
import * as Blockly from 'blockly/core';
import './language/loader';
import { Profile } from 'mixly';
import {
PythonVariablesBlocks,
PythonControlBlocks,
PythonMathBlocks,
PythonTextBlocks,
PythonListsBlocks,
PythonDictsBlocks,
PythonLogicBlocks,
PythonStorageBlocks,
PythonProceduresBlocks,
PythonTupleBlocks,
PythonSetBlocks,
PythonHtmlBlocks,
PythonUtilityBlocks,
PythonVariablesGenerators,
PythonControlGenerators,
PythonMathGenerators,
PythonTextGenerators,
PythonListsGenerators,
PythonDictsGenerators,
PythonLogicGenerators,
PythonStorageGenerators,
PythonProceduresGenerators,
PythonTupleGenerators,
PythonSetGenerators,
PythonHtmlGenerators,
PythonUtilityGenerators,
Procedures,
Variables,
Python
} from '@mixly/python';
import {
PythonMixpyAIBlocks,
PythonMixpyAlgorithmBlocks,
PythonMixpyCommunicateBlocks,
PythonMixpyCVBlocks,
PythonMixpyDataBlocks,
PythonMixpyDatastructureBlocks,
PythonMixpyFactoryBlocks,
PythonMixpyHardwareBlocks,
PythonMixpyInoutBlocks,
PythonMixpyIOTBlocks,
PythonMixpyPinsBlocks,
PythonMixpySerialBlocks,
PythonMixpySKLearnBlocks,
PythonMixpySystemBlocks,
PythonMixpyTurtleBlocks,
PythonMixpyAIGenerators,
PythonMixpyAlgorithmGenerators,
PythonMixpyCommunicateGenerators,
PythonMixpyCVGenerators,
PythonMixpyDataGenerators,
PythonMixpyDatastructureGenerators,
PythonMixpyFactoryGenerators,
PythonMixpyHardwareGenerators,
PythonMixpyInoutGenerators,
PythonMixpyIOTGenerators,
PythonMixpyPinsGenerators,
PythonMixpySerialGenerators,
PythonMixpySKLearnGenerators,
PythonMixpySystemGenerators,
PythonMixpyTurtleGenerators
} from '@mixly/python-mixpy';
import './others/loader';
import './css/color_mixpy_python_advance.css';
Object.assign(Blockly.Variables, Variables);
Object.assign(Blockly.Procedures, Procedures);
Blockly.Python = Python;
Blockly.generator = Python;
Profile.default = {};
Object.assign(
Blockly.Blocks,
PythonVariablesBlocks,
PythonControlBlocks,
PythonMathBlocks,
PythonTextBlocks,
PythonListsBlocks,
PythonDictsBlocks,
PythonLogicBlocks,
PythonStorageBlocks,
PythonProceduresBlocks,
PythonTupleBlocks,
PythonSetBlocks,
PythonHtmlBlocks,
PythonUtilityBlocks,
PythonMixpyAIBlocks,
PythonMixpyAlgorithmBlocks,
PythonMixpyCommunicateBlocks,
PythonMixpyCVBlocks,
PythonMixpyDataBlocks,
PythonMixpyDatastructureBlocks,
PythonMixpyFactoryBlocks,
PythonMixpyHardwareBlocks,
PythonMixpyInoutBlocks,
PythonMixpyIOTBlocks,
PythonMixpyPinsBlocks,
PythonMixpySerialBlocks,
PythonMixpySKLearnBlocks,
PythonMixpySystemBlocks,
PythonMixpyTurtleBlocks,
);
Object.assign(
Blockly.Python.forBlock,
PythonVariablesGenerators,
PythonControlGenerators,
PythonMathGenerators,
PythonTextGenerators,
PythonListsGenerators,
PythonDictsGenerators,
PythonLogicGenerators,
PythonStorageGenerators,
PythonProceduresGenerators,
PythonTupleGenerators,
PythonSetGenerators,
PythonHtmlGenerators,
PythonUtilityGenerators,
PythonMixpyAIGenerators,
PythonMixpyAlgorithmGenerators,
PythonMixpyCommunicateGenerators,
PythonMixpyCVGenerators,
PythonMixpyDataGenerators,
PythonMixpyDatastructureGenerators,
PythonMixpyFactoryGenerators,
PythonMixpyHardwareGenerators,
PythonMixpyInoutGenerators,
PythonMixpyIOTGenerators,
PythonMixpyPinsGenerators,
PythonMixpySerialGenerators,
PythonMixpySKLearnGenerators,
PythonMixpySystemGenerators,
PythonMixpyTurtleGenerators
);

View File

@@ -0,0 +1,5 @@
export const EnMsg = {
'PYTHON_PYODIDE_IMAGE': 'Image'
};
export const EnCatgories = {};

View File

@@ -0,0 +1,12 @@
import * as Blockly from 'blockly/core';
import { ZhHansMsg, ZhHansCatgories } from './zh-hans';
import { ZhHantMsg, ZhHantCatgories } from './zh-hant';
import { EnMsg, EnCatgories } from './en';
// 载入语言文件
Object.assign(Blockly.Lang.ZhHans, ZhHansMsg);
Object.assign(Blockly.Lang.ZhHant, ZhHantMsg);
Object.assign(Blockly.Lang.En, EnMsg);
Object.assign(Blockly.Lang.ZhHans.MSG, ZhHansCatgories);
Object.assign(Blockly.Lang.ZhHant.MSG, ZhHantCatgories);
Object.assign(Blockly.Lang.En.MSG, EnCatgories);

View File

@@ -0,0 +1,5 @@
export const ZhHansMsg = {
'PYTHON_PYODIDE_IMAGE': '图像'
};
export const ZhHansCatgories = {};

View File

@@ -0,0 +1,5 @@
export const ZhHantMsg = {
'PYTHON_PYODIDE_IMAGE': '影像'
};
export const ZhHantCatgories = {};

View File

@@ -0,0 +1,12 @@
{
"boardImg": "./media/webpy.png",
"boardType": "Python 3 Online",
"language": "Python",
"nav": {
"webrun": true,
"webcancel": true,
"save": {
"py": true
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{}

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -0,0 +1,3 @@
import NavExt from './nav-ext';
NavExt.init();

View File

@@ -0,0 +1,43 @@
import { app, Nav } from 'mixly';
import * as Blockly from 'blockly/core';
import PythonShell from './python-shell';
const NavExt = {};
NavExt.init = async function () {
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();
},
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();
},
scopeType: Nav.Scope.LEFT,
weight: 5
});
await PythonShell.init();
}
export default NavExt;

View File

@@ -0,0 +1,213 @@
import * as path from 'path';
// import * as dayjs from 'dayjs';
import {
Workspace,
Debug,
Env,
Msg
} from 'mixly';
import { KernelLoader } from '@basthon/kernel-loader';
import StatusBarImage from './statusbar-image';
class PythonShell {
static {
this.pythonShell = null;
this.init = async function () {
StatusBarImage.init();
const projectPath = path.relative(Env.indexDirPath, Env.boardDirPath);
const loader = new KernelLoader({
pyodideURLs: [path.join(projectPath, 'deps/0.62.21/python3/pyodide/pyodide.js')],
rootPath: 'http://download.mixlylibs.cloud/web-python3-deps',
language: 'python3',
});
const kernel = await loader.kernelAvailable();
if (!kernel) {
return;
}
await kernel.init();
await kernel.loaded();
this.loader = loader;
this.kernel = kernel;
this.pythonShell = new PythonShell();
this.pyodide = window.pyodide;
this.interruptBuffer = new Uint8Array(new ArrayBuffer(1));
this.pyodide.setInterruptBuffer(this.interruptBuffer);
}
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;
#kernel_ = 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 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.#kernel_ = PythonShell.kernel;
this.#addEventsListener_();
}
#addEventsListener_() {
this.#kernel_.addEventListener('eval.finished', () => {
this.#running_ = false;
this.#statusBarTerminal_.addValue(`\n==${Msg.Lang['shell.finish']}==`);
});
this.#kernel_.addEventListener('eval.output', (data) => {
this.#statusBarTerminal_.addValue(data.content);
});
this.#kernel_.addEventListener('eval.error', () => {
this.#running_ = false;
this.#statusBarTerminal_.addValue(`\n==${Msg.Lang['shell.finish']}==`);
});
this.#kernel_.addEventListener('eval.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_();
});
this.#kernel_.addEventListener('eval.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);
}
run(code) {
this.stop()
.then(() => {
this.#statusBarsManager_.changeTo('output');
this.#statusBarsManager_.show();
this.#statusBarTerminal_.setValue(`${Msg.Lang['shell.running']}...\n`);
this.#running_ = true;
this.#kernel_.dispatchEvent('eval.request', {
code,
interactive: false,
});
})
.catch(Debug.error);
}
async stop() {
if (this.#waittingForInput_) {
this.#exitInput_();
}
if (this.#running_) {
const timeout = 5;
PythonShell.interruptBuffer[0] = 2;
const startTime = Number(new Date());
while (Number(new Date()) - startTime < timeout * 1000) {
if (this.#running_) {
PythonShell.interruptBuffer[0] = 2;
await this.sleep(100);
} else {
break;
}
}
this.#running_ = false;
}
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
export default PythonShell;

View File

@@ -0,0 +1,169 @@
import * as Blockly from 'blockly/core';
import {
PageBase,
HTMLTemplate,
StatusBarsManager,
Workspace
} from 'mixly';
import $ from 'jquery';
import '../language/loader';
import STATUS_BAR_IMAGE_TEMPLATE from '../templates/html/statusbar-image.html';
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', Blockly.Msg.PYTHON_PYODIDE_IMAGE);
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;

View File

@@ -0,0 +1,41 @@
{
"name": "@mixly/python-pyodide",
"version": "1.0.0",
"description": "适用于mixly的python pyodide模块",
"scripts": {
"build:dev": "webpack --config=webpack.dev.js",
"build:prod": "webpack --config=webpack.prod.js",
"build:examples": "node ../../../scripts/build-examples.js -t special",
"publish:board": "npm publish --registry https://registry.npmjs.org/"
},
"devDependencies": {
"buffer": "^6.0.3",
"crypto-browserify": "^3.12.0",
"path-browserify": "^1.0.1",
"stream-browserify": "^3.0.0",
"vm-browserify": "^1.1.2"
},
"dependencies": {
"@basthon/kernel-loader": "^0.62.21"
},
"main": "./export.js",
"author": "Mixly Team",
"keywords": [
"mixly",
"mixly-plugin",
"python-pyodide"
],
"homepage": "https://gitee.com/bnu_mixly/mixly3/tree/master/boards/default_src/python_pyodide",
"bugs": {
"url": "https://gitee.com/bnu_mixly/mixly3/issues"
},
"repository": {
"type": "git",
"url": "https://gitee.com/bnu_mixly/mixly3.git",
"directory": "default_src/python_pyodide"
},
"publishConfig": {
"access": "public"
},
"license": "Apache 2.0"
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
<style>
div[m-id="{{d.mId}}"] {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
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;
}
</style>
<div m-id="{{d.mId}}" class="page-item">
</div>

View File

@@ -0,0 +1,5 @@
import signal
def signal_handler(signal, frame):
raise ValueError("程序中断")
signal.signal(signal.SIGINT, signal_handler)

View File

@@ -0,0 +1,34 @@
const path = require("path");
const common = require("../../../webpack.common");
const { merge } = require("webpack-merge");
module.exports = merge(common, {
resolve: {
alias: {
'@mixly/python': path.resolve(__dirname, '../python'),
'@mixly/python-mixpy': path.resolve(__dirname, '../python_mixpy')
},
extensions: ['.ts', '.js'],
fallback: {
// for ocaml bundle
constants: require.resolve('constants-browserify'),
tty: require.resolve('tty-browserify'),
vm: require.resolve('vm-browserify'),
fs: false,
child_process: false,
// for sql bundle
crypto: require.resolve('crypto-browserify'),
path: require.resolve('path-browserify'),
buffer: require.resolve('buffer/'),
stream: require.resolve('stream-browserify'),
}
},
module: {
rules: [
{
resourceQuery: /asset-url/,
type: 'asset/resource',
}
]
}
});

View File

@@ -0,0 +1,21 @@
const path = require("path");
const common = require("./webpack.common");
const { merge } = require("webpack-merge");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = merge(common, {
mode: "development",
devtool: 'source-map',
plugins: [
new ESLintPlugin({
context: process.cwd(),
}),
new HtmlWebpackPlugin({
inject: false,
template: path.resolve(process.cwd(), 'template.xml'),
filename: 'index.xml',
minify: false
}),
]
});

View File

@@ -0,0 +1,27 @@
const path = require("path");
const common = require("./webpack.common");
const { merge } = require("webpack-merge");
const TerserPlugin = require("terser-webpack-plugin");
var HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = merge(common, {
mode: "production",
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
extractComments: false,
}),
new HtmlWebpackPlugin({
inject: false,
template: path.resolve(process.cwd(), 'template.xml'),
filename: 'index.xml',
minify: {
removeAttributeQuotes: true,
collapseWhitespace: true,
removeComments: true,
}
})
]
}
});