I am creating a Pyodide Ace-editor Python code runner in which anything that is typed within ace pyodide will run. It works pretty well, however I am having an incredible amount of difficulty trying to put keyinterrupts in my current implementation as I use a text area to show output instead of Pyodide.console: https://pyodide.org/en/stable/usage/api/python-api/console.html. I want to effectivley redirect everything that I was outputting into textarea into pyodide console so I would be able to have keyinterrupts (atleast I believe it supports it). How would I be able to do it? Here is my code:
runner.html:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Pyodide in Action</title>
<link rel="stylesheet" href="runner_style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.4/ace.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.4/ext-static_highlight.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.4/ext-modelist.js"></script>
<script src="https://unpkg.com/file-saver"></script>
<script src="https://cdn.jsdelivr.net/pyodide/v0.23.2/full/pyodide.js"></script>
</head>
<body>
<div class="container">
<div class="one">
<div id="editor" style="width: 900px;height:200px;"></div>
</div>
</div>
<textarea readonly rows=10 cols=30 id=output style="margin: 0px; width: 900px; height: 180px; font-size: 16px;">Loading and compiling...</textarea><br>
<div class="buttonContainer">
<button id="runButton">Run</button>
<button id="clearButton">Clear</button>
</div>
<script type="text/javascript" src="runner.js"></script>
</body>
</html>
runner.js:
var editor = ace.edit("editor");
var output_pane;
function setParams() {
const queryString = new URLSearchParams(location.search);
const initCode = queryString.get('initCode');
if (initCode) {
const decodedCode = decodeURIComponent(initCode);
editor.setValue(decodedCode);
}
}
loadPyodide().then((pyodide) => {
// pyodide is now ready to use...
globalThis.pyodide = pyodide;
appendOutput('Python ready.\n');
});
// Override console.warn so that anything logged will go to textarea
console.warn = function(message) {
console.log(message);
};
function appendOutput(msg) {
// used to add program output to the textarea
output_pane.value = output_pane.value + '\n' + msg;
output_pane.scrollTop = output_pane.scrollHeight;
}
function configEditor(){
// configure the ace editor to make it usable
editor = ace.edit("editor");
editor.setTheme("ace/theme/xcode");
editor.session.setMode("ace/mode/python");
editor.setShowPrintMargin(false);
editor.setBehavioursEnabled(true);
editor.setFontSize(13);
editor.setAnimatedScroll(true);
editor.setAutoScrollEditorIntoView(true);
setParams();
}
function openCode(filePathToUse) {
getCode(filePathToUse)
.then(code => {
var modelist = ace.require("ace/ext/modelist");
var modeName = modelist.getModeForPath(filePathToUse).mode;
editor.session.setMode(modeName);
editor.session.setValue(code);
})
.catch(error => {
console.error('Error occurred while opening the code:', error);
});
}
async function runCode(code_to_run) {
// Run the code thats within the editor so students can test
console.logs = [];
let promise = new Promise((resolve, reject) => {
window.pyodide.runPython(code_to_run)
resolve(true)
}).catch(err => {
console.log(err);
appendOutput(console.logs.join('\n'));
});
let result = await promise;
if (result) {
appendOutput(console.logs.join('\n'));
}
}
//make a function getCode that takes in a file path and returns the code in that file as a string to use in ace
async function getCode(codeToGet) {
try {
const response = await fetch(codeToGet);
const data = await response.text();
return data;
} catch (error) {
console.error('Error occurred while opening the code:', error);
}
}
document.addEventListener('DOMContentLoaded', (event) => {
output_pane = document.getElementById("output");
// Add event listeners for running the code the user types
document.getElementById("runButton").addEventListener('click', function () {
runCode(editor.getValue());
});
// Add event listeners for the clear button
document.getElementById("clearButton").addEventListener('click', function () {
output_pane.value = '';
});
// Capture the output from Pyodide and add it to an array
console.stdlog = console.log.bind(console);
console.logs = [];
console.log = function(){
console.logs.push(Array.from(arguments));
console.stdlog.apply(console, arguments);
}
configEditor();
});
my css file if you want to completely replicate it:
#editor {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 1225px;
height: 200px;
}
.container {
width: 80%;
height: 200px;
margin: auto;
padding: 10px;
}
.one {
width: 50%;
height: 100px;
float: left;
}
.two {
margin-left: 50%;
height: 500px;
}
.buttonContainer {
width: 100%;
text-align: left;
display: inline
}
textarea {
margin: 0px;
width: 750px;
height: 200px;
padding: 10px; /* Add padding inside the textarea */
font-family: Arial, sans-serif; /* Set the font-family */
font-size: 15px; /* Set the font-size */
background-color: #fff; /* Set the background color */
border: 1px solid #ccc; /* Set the border */
border-radius: 4px; /* Add some border-radius */
resize: vertical; /* Allow vertical resizing only */
outline: none; /* Remove the default focus outline */
transition: border-color 0.3s; /* Add transition effect to border color */
}
textarea:focus {
border-color: #007bff;
}
#runButton, #clearButton {
border-radius: 4px; /* Adjust the value to change the roundness of the corners */
padding: 10px 20px;
background-color: #ffffff;
color: black;
font-size: 15px;
border: solid;
cursor: pointer;
display: inline-block;
margin-right: 5px;
margin-bottom: 5px;
}
#runButton:hover, #clearButton:hover {
background-color: #ddd;
color: black;
transition: background-color 0.2s, color 0.2s;
}
I tried to follow Pyodides documentation on how console works to try to replicate the look of this example: https://pyodide.org/en/stable/console.html, except just have my code outputted there but there seems to be sparse documentation on how to do so.