|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html>
|
|
|
|
|
<head>
|
|
|
|
|
<meta charset="utf-8">
|
|
|
|
|
<title>Mr. pyTermTk Sandbox</title>
|
|
|
|
|
<link rel="icon" type="image/x-icon" href="www/favicon.ico">
|
|
|
|
|
|
|
|
|
|
<script src="www/pyodide/pyodide.js"></script>
|
|
|
|
|
|
|
|
|
|
<link href="www/xterm/xterm.css" rel="stylesheet" />
|
|
|
|
|
<script src="www/xterm/xterm.js"></script>
|
|
|
|
|
<script src="www/xterm-addon-fit/xterm-addon-fit.js"></script>
|
|
|
|
|
<script src="www/xterm-addon-unicode11/xterm-addon-unicode11.js"></script>
|
|
|
|
|
|
|
|
|
|
<link href="www/fontawesome/fontawesome.min.css" rel="stylesheet">
|
|
|
|
|
<link href="www/fontawesome/regular.min.css" rel="stylesheet">
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript" src="www/w2ui/w2ui-2.0.min.js"></script>
|
|
|
|
|
<link rel="stylesheet" type="text/css" href="www/w2ui/w2ui-2.0.min.css" />
|
|
|
|
|
|
|
|
|
|
<link href="www/codemirror/theme/mbo.css" rel="stylesheet" >
|
|
|
|
|
<link href="www/codemirror/codemirror.css" rel="stylesheet" />
|
|
|
|
|
<script src="www/codemirror/codemirror.js"></script>
|
|
|
|
|
<script src="www/codemirror/modes/python.js"></script>
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
.CodeMirror { height: 100%; }
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
|
|
|
|
|
<div id="layout" style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px"></div>
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
|
|
|
let pstyle = 'border: 1px solid #efefef; padding: 5px;';
|
|
|
|
|
let expand = 'position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px';
|
|
|
|
|
new w2layout({
|
|
|
|
|
box: '#layout',
|
|
|
|
|
name: 'layout_pyTermTk_sandbox',
|
|
|
|
|
padding: 4,
|
|
|
|
|
panels: [
|
|
|
|
|
//{ type: 'top', size: 50, resizable: true, style: pstyle, html: 'top' },
|
|
|
|
|
{ type: 'left', size: 200, resizable: true, style: pstyle,
|
|
|
|
|
html: '<div id="sidebar" style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px"></div>' },
|
|
|
|
|
{ type: 'main', style: pstyle,
|
|
|
|
|
toolbar: {
|
|
|
|
|
items: [
|
|
|
|
|
{ type: 'button', id: 'run_button', text: 'Run', icon: 'far fa-play-circle' },
|
|
|
|
|
{ type: 'html', id: 'uri',
|
|
|
|
|
html(item) {
|
|
|
|
|
let html =
|
|
|
|
|
'<div style="padding: 0px 10px; margin-top: -2px;" >'+
|
|
|
|
|
' URI: <input id="codeUri" size="50"/>'+
|
|
|
|
|
' FPS Cap: <input id="fpsCap" value="30" size="1">'+
|
|
|
|
|
'</div>';
|
|
|
|
|
return html;
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
onClick(event) { run(); }
|
|
|
|
|
},
|
|
|
|
|
html: '<div id="codeArea" style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 25px"></div>'+
|
|
|
|
|
`<div id="codeArea" style="position: absolute; left: 15px; right: 0px; bottom: 0px">
|
|
|
|
|
<a href="https://github.com/ceccopierangiolieugenio/pyTermTk">pyTermTk</a> sandbox,
|
|
|
|
|
Powered by <a href="https://pyodide.org/">Pyodide</a>
|
|
|
|
|
and <a href="https://xtermjs.org">xterm.js</a>
|
|
|
|
|
and <a href="https://codemirror.net/5/">CodeMirror5</a>
|
|
|
|
|
and <a href="https://w2ui.com/">w2ui</a>
|
|
|
|
|
</div>`},
|
|
|
|
|
{ type: 'right', size: 1000, resizable: true, style: pstyle,
|
|
|
|
|
html: '<div id="terminal" oncontextmenu="return false;" style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px"></div>' }
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
new w2field({ el: query('#fpsCap')[0], type: 'int', autoFormat: false })
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
|
|
|
/* xterm.js demo */
|
|
|
|
|
/* https://www.npmjs.com/package/xterm-addon-fit */
|
|
|
|
|
const fitAddon = new FitAddon.FitAddon();
|
|
|
|
|
/* https://www.npmjs.com/package/xterm-addon-unicode11 */
|
|
|
|
|
const unicode11Addon = new Unicode11Addon.Unicode11Addon();
|
|
|
|
|
|
|
|
|
|
var term = new Terminal({allowProposedApi: true});
|
|
|
|
|
term.loadAddon(fitAddon);
|
|
|
|
|
term.loadAddon(unicode11Addon);
|
|
|
|
|
|
|
|
|
|
term.unicode.activeVersion = '11';
|
|
|
|
|
|
|
|
|
|
term.open(document.getElementById('terminal'));
|
|
|
|
|
|
|
|
|
|
term.write('xterm.js - Loaded\n\r')
|
|
|
|
|
|
|
|
|
|
fitAddon.fit()
|
|
|
|
|
|
|
|
|
|
w2ui.layout_pyTermTk_sandbox.on('resize', (event) => {
|
|
|
|
|
setTimeout(()=>{fitAddon.fit()},0.5)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Code Mirror */
|
|
|
|
|
let myCodeMirror = CodeMirror(document.getElementById('codeArea'), {
|
|
|
|
|
mode: "python",
|
|
|
|
|
lineNumbers: true,
|
|
|
|
|
styleActiveLine: true,
|
|
|
|
|
matchBrackets: true
|
|
|
|
|
});
|
|
|
|
|
myCodeMirror.setOption("theme", "mbo");
|
|
|
|
|
let getCode = function(){
|
|
|
|
|
return myCodeMirror.getValue()
|
|
|
|
|
}
|
|
|
|
|
let setCode = function(txt){
|
|
|
|
|
myCodeMirror.setValue(txt)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* pyodide demo */
|
|
|
|
|
var pyodide = null
|
|
|
|
|
var run = null
|
|
|
|
|
var namespace = null
|
|
|
|
|
async function main(){
|
|
|
|
|
pyodide = await loadPyodide();
|
|
|
|
|
|
|
|
|
|
let pyodideProxy = {
|
|
|
|
|
consoleLog: function(m){
|
|
|
|
|
console.log("TTk:",m)
|
|
|
|
|
},
|
|
|
|
|
termPush: function (s) {
|
|
|
|
|
term.write(s);
|
|
|
|
|
},
|
|
|
|
|
termSize: function () {
|
|
|
|
|
return [term.cols, term.rows]
|
|
|
|
|
},
|
|
|
|
|
setTimeout: function(t, i) {
|
|
|
|
|
// console.log("TIME (Start)",i,t)
|
|
|
|
|
return setTimeout(() => ttk_timer(i), t)
|
|
|
|
|
},
|
|
|
|
|
stopTimeout: function(t) {
|
|
|
|
|
// console.log("TIME (Stop)",t)
|
|
|
|
|
clearTimeout(t)
|
|
|
|
|
},
|
|
|
|
|
clearTimeout: function(){
|
|
|
|
|
let highestTimeoutId = setTimeout(";");
|
|
|
|
|
for (let i = 0 ; i < highestTimeoutId ; i++) {
|
|
|
|
|
clearTimeout(i);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
setInterval: function(t, i) {
|
|
|
|
|
setTinterval(() => console.log('WIP -> Interval' + i), t)
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
term.write('Pyodide ('+pyodide.version+') - Loaded\n\r')
|
|
|
|
|
|
|
|
|
|
pyodide.registerJsModule("pyodideProxy", pyodideProxy);
|
|
|
|
|
|
|
|
|
|
term.write('Pyodide Proxy - Loaded\n\r')
|
|
|
|
|
|
|
|
|
|
let zipResponse = await fetch("bin/TermTk.tgz");
|
|
|
|
|
let zipBinary = await zipResponse.arrayBuffer();
|
|
|
|
|
pyodide.unpackArchive(zipBinary, ".tar.gz");
|
|
|
|
|
|
|
|
|
|
term.write('TermTk - Loaded\n\r')
|
|
|
|
|
|
|
|
|
|
zipResponse = await fetch("bin/demo.tgz");
|
|
|
|
|
zipBinary = await zipResponse.arrayBuffer();
|
|
|
|
|
pyodide.unpackArchive(zipBinary, ".tar.gz");
|
|
|
|
|
|
|
|
|
|
term.write('Demos - Loaded\n\r')
|
|
|
|
|
|
|
|
|
|
zipResponse = await fetch("bin/tutorial.tgz");
|
|
|
|
|
zipBinary = await zipResponse.arrayBuffer();
|
|
|
|
|
pyodide.unpackArchive(zipBinary, ".tar.gz");
|
|
|
|
|
|
|
|
|
|
term.write('Tutorials - Loaded\n\r')
|
|
|
|
|
|
|
|
|
|
/* Sidebar
|
|
|
|
|
Fetch all the files in the pyodide.FS
|
|
|
|
|
And push them in the sidebar
|
|
|
|
|
*/
|
|
|
|
|
let getAllFiles = function(p){
|
|
|
|
|
let ls = pyodide.FS.readdir(p)
|
|
|
|
|
let ret = []
|
|
|
|
|
for(let i=0 ; i<ls.length; i++){
|
|
|
|
|
let n = p+"/"+ls[i]
|
|
|
|
|
if(ls[i]=='.' || ls[i]=='..' || n==(pyodide.FS.currentPath+"/TermTk")) continue;
|
|
|
|
|
if( pyodide.FS.isDir(pyodide.FS.lstat(n).mode)){
|
|
|
|
|
ret.push({id:n, text:ls[i], expanded: true, nodes:getAllFiles(n)})
|
|
|
|
|
}else{
|
|
|
|
|
if(n.endsWith('.py')){
|
|
|
|
|
ret.push({id:n, text:ls[i]})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ret
|
|
|
|
|
}
|
|
|
|
|
let files = getAllFiles(pyodide.FS.currentPath)
|
|
|
|
|
|
|
|
|
|
new w2sidebar({
|
|
|
|
|
box: '#sidebar',
|
|
|
|
|
name: 'sidebar',
|
|
|
|
|
nodes: files })
|
|
|
|
|
|
|
|
|
|
w2ui.sidebar.on('click', function (event) {
|
|
|
|
|
console.log('Last Event: ' + event.type + ' Target: ' + event.target);
|
|
|
|
|
loadFile(event.target)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var loadFile = function(f){
|
|
|
|
|
let content = pyodide.FS.readFile(f, {encoding:'utf8'})
|
|
|
|
|
setCode(content)
|
|
|
|
|
document.getElementById("codeUri").value = f
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check the "fileUri" field in the address
|
|
|
|
|
and load it if defined */
|
|
|
|
|
const queryString = window.location.search;
|
|
|
|
|
console.log(queryString);
|
|
|
|
|
const urlParams = new URLSearchParams(queryString);
|
|
|
|
|
fileUri = urlParams.get("fileUri")
|
|
|
|
|
filePath = urlParams.get("filePath")
|
|
|
|
|
if (fileUri != null){
|
|
|
|
|
pyodide.FS.writeFile(pyodide.FS.currentPath+"/test_file.py", await (await fetch(fileUri)).text());
|
|
|
|
|
loadFile(pyodide.FS.currentPath+"/test_file.py")
|
|
|
|
|
}else if (filePath != null){
|
|
|
|
|
loadFile(pyodide.FS.currentPath+"/"+filePath)
|
|
|
|
|
}else{
|
|
|
|
|
loadFile(pyodide.FS.currentPath+"/demo/demo.py")
|
|
|
|
|
}
|
|
|
|
|
//loadFile("/home/pyodide/tutorial/calculator/calculator.005.py")
|
|
|
|
|
w2ui.sidebar.select(pyodide.FS.currentPath+"/demo/demo.py")
|
|
|
|
|
|
|
|
|
|
term.write('Starting Demo...\n\r')
|
|
|
|
|
|
|
|
|
|
namespace = pyodide.globals.get("dict")();
|
|
|
|
|
pyodide.runPython(`
|
|
|
|
|
import sys
|
|
|
|
|
import TermTk as ttk
|
|
|
|
|
from TermTk.TTkCore.TTkTerm.input import TTkInput
|
|
|
|
|
import pyodideProxy
|
|
|
|
|
|
|
|
|
|
def ttk_input(val):
|
|
|
|
|
kevt,mevt,paste = TTkInput.key_process(val)
|
|
|
|
|
if kevt or mevt:
|
|
|
|
|
TTkInput.inputEvent.emit(kevt, mevt)
|
|
|
|
|
if paste:
|
|
|
|
|
TTkInput.pasteEvent.emit(paste)
|
|
|
|
|
|
|
|
|
|
def ttk_resize(w,h):
|
|
|
|
|
ttk.TTkLog.debug(f"Resize: {w=} {h=}")
|
|
|
|
|
if ttk.TTkHelper._rootWidget:
|
|
|
|
|
ttk.TTkHelper._rootWidget._win_resize_cb(w,h)
|
|
|
|
|
ttk.TTkHelper.rePaintAll()
|
|
|
|
|
ttk.TTkTerm.cont()
|
|
|
|
|
|
|
|
|
|
def ttk_timer(tid):
|
|
|
|
|
ttk.TTkTimer.triggerTimerId(tid)
|
|
|
|
|
|
|
|
|
|
def ttk_log(val):
|
|
|
|
|
# hex = [f"0x{ord(x):02x}" for x in val]
|
|
|
|
|
ttk.TTkLog.debug("---> "+val.replace("\\033","<ESC>") + " - ")
|
|
|
|
|
ttk.TTkHelper.paintAll()
|
|
|
|
|
|
|
|
|
|
def ttk_clean():
|
|
|
|
|
if ttk.TTkHelper._rootWidget:
|
|
|
|
|
ttk.TTkTimer.pyodideQuit()
|
|
|
|
|
ttk.TTkHelper._rootWidget.quit()
|
|
|
|
|
ttk.TTkHelper._focusWidget = None
|
|
|
|
|
ttk.TTkHelper._rootCanvas = None
|
|
|
|
|
ttk.TTkHelper._rootWidget = None
|
|
|
|
|
ttk.TTkHelper._updateBuffer = set()
|
|
|
|
|
ttk.TTkHelper._updateWidget = set()
|
|
|
|
|
ttk.TTkHelper._overlay = []
|
|
|
|
|
ttk.TTkHelper._shortcut = []
|
|
|
|
|
ttk.TTkLog._messageHandler = [message_handler]
|
|
|
|
|
|
|
|
|
|
def ttk_init():
|
|
|
|
|
ttk.TTkToolTip.toolTipTimer = ttk.TTkTimer()
|
|
|
|
|
ttk.TTkToolTip.toolTipTimer.timeout.connect(ttk.TTkToolTip._toolTipShow)
|
|
|
|
|
|
|
|
|
|
def message_handler(mode, context, message):
|
|
|
|
|
msgType = "DEBUG"
|
|
|
|
|
if mode == ttk.TTkLog.InfoMsg: msgType = "[INFO]"
|
|
|
|
|
elif mode == ttk.TTkLog.WarningMsg: msgType = "[WARNING]"
|
|
|
|
|
elif mode == ttk.TTkLog.CriticalMsg: msgType = "[CRITICAL]"
|
|
|
|
|
elif mode == ttk.TTkLog.FatalMsg: msgType = "[FATAL]"
|
|
|
|
|
elif mode == ttk.TTkLog.ErrorMsg: msgType = "[ERROR]"
|
|
|
|
|
pyodideProxy.consoleLog(f"{msgType} {context.file} {message}")
|
|
|
|
|
# Register the callback to the message handler
|
|
|
|
|
ttk.TTkLog.installMessageHandler(message_handler)
|
|
|
|
|
`,{ globals: namespace }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let ttk_log = namespace.get("ttk_log");
|
|
|
|
|
let ttk_input = namespace.get("ttk_input");
|
|
|
|
|
let ttk_timer = namespace.get("ttk_timer");
|
|
|
|
|
let ttk_resize = namespace.get("ttk_resize");
|
|
|
|
|
let ttk_clean = namespace.get("ttk_clean");
|
|
|
|
|
let ttk_init = namespace.get("ttk_init");
|
|
|
|
|
|
|
|
|
|
term.onResize( (obj) => {
|
|
|
|
|
term.reset()
|
|
|
|
|
ttk_resize(obj.cols, obj.rows)
|
|
|
|
|
});
|
|
|
|
|
term.onData((d, evt) => { ttk_input(d) })
|
|
|
|
|
|
|
|
|
|
pyodide.runPython(`
|
|
|
|
|
import sys,os
|
|
|
|
|
sys.path.append(os.path.join(sys.path[0],'demo'))
|
|
|
|
|
__name__ = "__main__"
|
|
|
|
|
`,{ globals: namespace }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
run = function(){
|
|
|
|
|
ttk_clean()
|
|
|
|
|
console.log("Run App")
|
|
|
|
|
|
|
|
|
|
let filename = document.getElementById("codeUri").value
|
|
|
|
|
let fps = document.getElementById("fpsCap").value
|
|
|
|
|
let pwd = pyodide.PATH.dirname(filename)
|
|
|
|
|
|
|
|
|
|
pyodide.runPython(`
|
|
|
|
|
__file__='`+filename+`'
|
|
|
|
|
os.chdir('`+pwd+`')
|
|
|
|
|
ttk.TTkCfg.maxFps = `+fps,{ globals: namespace })
|
|
|
|
|
|
|
|
|
|
ttk_init()
|
|
|
|
|
|
|
|
|
|
let content = getCode()
|
|
|
|
|
pyodide.runPython(content,{ globals: namespace });
|
|
|
|
|
|
|
|
|
|
ttk_log(filename + " - LOADED")
|
|
|
|
|
};
|
|
|
|
|
run()
|
|
|
|
|
}
|
|
|
|
|
main()
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|