前言
初学者学习编程语言时,通常需要安装编程语言对应的环境,以Python为例,要学习Python,你就需要下载Python解释器并安装到本地,对于没有编程经验的人而言,这一步难倒了很多人,所以很多编程学习网站提供了在线编程的功能,学习者可以在网站上直接编写Python代码,然后点击运行,便可以在网页中得到相应的结果,这是怎么做到的?
最近刚好在做直播任务,即每周要做一定量的抖音直播,昨日直播时,临时起意,打算实现这个功能并将查资料、研究、开发的过程作为直播内容,开播时,我也不知道自己能不能弄出来,但因为刚做直播没多久,没啥人看,也就没啥包袱,直接开整,最后还是让我弄出来了。
本篇文章就记录一下开发过程。
前端开发
经过简单的调研,放弃一开始想用Vue3开发的想法,直接使用HTML、CSS、JS搞则可,再上个Jquery,来使用Ajax,大体的流程为:
Code => Ajax => Python Web(Flask) => Run Code => Result
代码通过Ajax发给后端服务(Flask构建),不同编程语言使用不同的解释器去执行相应的代码,将执行的结果返回。
创建online-ide的目录,在目录下,构建如下结构:
C:\USERS\ADMIN\WORKPLACE\ONLINE-IDE
├─back-end
├─serve.py
└─front-end
├─ide.html
├─css
└─style.css
├─js
└─ide.js
└─lib
front-end用于放前端代码,back-end用于放后端代码,我们将页面的主代码写到ide.html中,代码非常简单,就是用div构建不同的部分,然后用css简单修饰一下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Online IDE</title>
<!-- <link rel="stylesheet" href="css/style.css"/> -->
<link rel="stylesheet" href="front-end/css/style.css">
</head>
<body>
<div class="header"> Online IDE </div>
<div class="control-panel">
<!-- 选择编程语言 -->
选择编程语言:
<select name="languages" id="languages" class="languages" onchange="changeLanguage()">
<option value="c">C</option>
<option value="cpp">C++</option>
<option value="nodejs">Node.js</option>
<option value="python">Python</option>
</select>
</div>
<div class="editor" id="editor">
<!-- 编写代码的地方 -->
</div>
<div class="button-container">
<!-- 点击按钮,运行代码 -->
<button class="btn" onclick="runCode()"> 运行 </button>
</div>
<div class="output">
<!-- 代码结果输出 -->
</div>
</body>
</html>
代码中有相应的注释,就不赘述了。
这里有个核心就是前端要有代码编辑区域,单纯的div是无法满足代码编辑需求的,比如代码高亮,简单的语法报错等,通过简单搜索,发现ace(https://github.com/ajaxorg/ace)这个项目,该项目通过JS实现了支持代码高亮的代码编辑器。
阅读ace的文档发现项目作者提供了打包好的版本ace-builds(https://github.com/ajaxorg/ace-builds/),我们可以直接使用。
通过git clone拉取ace-builds项目,然后将src中的所有代码文件都赋值front-end/lib中,这些就是不同编程语言对应着其中的文件。
获取了ace后,我们还需要使用它,在ide.js中,我们写入如下代码:
let editor;
window.onload = function() {
// 载入ace
editor = ace.edit("editor");
// 设置编辑器主题
editor.setTheme("ace/theme/monokai");
// 设置编辑器解析的编程语言
editor.session.setMode("ace/mode/c_cpp");
}
// 选择不同编程语言时,需要切换ace的mode
function changeLanguage() {
let language = $("#languages").val();
if (language == 'c' || language == 'cpp') {
editor.session.setMode("ace/mode/c_cpp");
}
else if (language == 'python'){
editor.session.setMode("ace/mode/python");
}
else if (language == 'nodejs'){
editor.session.setMode("ace/mode/javascript");
};
}
// 用户写完代码,点击运行时,将代码通过Ajax发送至Python后端
function runCode() {
$.ajax({
url: "/run_code",
method: "POST",
data: {
language: $("#languages").val(),
code: editor.getSession().getValue()
},
success: function(response) {
console.log('success');
console.log(response);
$('.output').text(response);
}
})
}
至此,前端就开发完了。
后端开发
后端我选择Flask来开发,非常快,直接基于Flask docs里提供的代码,复制粘贴,再修修改改则可,我们将代码写到back-end的serve.py中,代码如下:
import subprocess
import os
import time
from flask import Flask
from flask import render_template
from flask import request
template_dir = os.path.abspath("../front-end")
static_folder = os.path.abspath("../front-end")
app = Flask(__name__, static_folder=static_folder, template_folder=template_dir)
@app.route("/run_code", methods=['GET', 'POST'])
def run_code():
if request.method == 'POST':
"""
1.接收到前端信息
2.将其存入文件中
3.使用相关的解释器去执行文件中的代码
4.将执行的结果返回给前端
"""
code = request.values.get('code')
language = request.values.get('language')
code_file_path = os.path.join(os.path.abspath('codes')) + str(time.time()) + '.code'
with open(code_file_path, 'w') as f:
f.write(code)
if language == 'python':
py_path = 'c:\\users\\admin\\appdata\\local\\programs\\python\\python38\\python.exe'
process = subprocess.Popen([py_path, code_file_path],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = process.communicate()
output = stdout + stderr
elif language == 'nodejs':
node_path = r'C:\Program Files\nodejs\node.exe'
process = subprocess.Popen([node_path, code_file_path],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = process.communicate()
output = stdout + stderr
return output
elif request.method == 'GET':
return render_template("ide.html", name="run code")
if __name__ == "__main__":
app.run(port=8001)
上述代码没啥难度,就是设置了一个run_code接口,如果是GET请求,则返回HTML页面(ide.html),如果是POST,则按编程语言的类型选择相应的解释器,去执行相应的代码。
通过Python的subprocess,利用shell的形式执行代码。
效果
运行项目,先用Python来实现斐波那契数列,效果如下:
换成JavaScript来实现斐波那契数列,效果如下:
结尾
哦豁,实现下来,比我想象的简单,整个直播的过程对我来说有点像限时编程,播完后,有点累,但感觉开发出来挺爽的。
Enjoy Code,我们下篇文章见。