把代码执行演示嵌在你的PPT中


声明:本文转载自https://my.oschina.net/taogang/blog/1530258,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

“Talk is cheap, show me your code!”

当一个程序员在做技术分享的时候, 代码演示经常是不可或缺的一个环节。然而在你的演示PPT和代码运行之间切换是一件非常恼人事情,而且非常影响演示的节奏和流畅性。要做一个完美的技术分享,能不能把代码的运行嵌入到PPT中呢?

当然可以,我前不久在公司内部分享了一起关于Python的小谜题,大家可以到斗鱼去观看这次分享的视频录播 (很糟糕的是在36分钟后,摄像头偏移了,拍了大半天挂钟)

为了实现代码嵌入ppt,我用到的关键技术包括:

首先,演示常用的是微软的PowerPoint(至于苹果的Keynote,我暂时还没有找到解决方案),所以你需要安装PowerPoint,在最新的Office套件中,微软提供了一个Webview的插件,有了这个插件,可以直接把一个Web页面,嵌入到Office文档中,自然也就包含了PPT。

注意,为了安全性的考虑,嵌入的web页面必须是https的。

好了有了这个,我们就可以把一个代码运行的web页面直接嵌入到演示文档中,剩下的事情,就是做一个可以运行不同代码的Web应用了。

Glot 是一个开源的在线代码运行平台。其架构如下图所示。

利用Glot的代码运行的核心功能,我们就可以很方便的开发一个可以运行各种代码的web应用。

利用容器,我们可以把整个应用分为以下的几个层次:

  • Base
    提供基本的代码运行的环境,包含了代码执行的必要的解释器和编译器。在本次演示中,我使用了golang:latest,但是碰巧的是这个镜像是拥有Python的解释器的,我的代码演示都是python,省去了我安装Python的步骤。如果是别的不同的语言的演示,注意安装对应的环境。
  • Code Runner https://github.com/gangtao/code_runner
    我的Code Runner是对Glot的Code Runner的一个增强,该项目提供一个运行代码的服务。
    项目的Dockerfile:
    FROM golang:latest  MAINTAINER gangtao@outlook.com  ENV GOPATH=/home/glot ENV GOROOT=/usr/local/go  # Add user RUN groupadd glot RUN useradd -m -d /home/glot -g glot -s /bin/bash glot  # Copy files Add ./build/release/server /home/glot/ # Add ./vendor/. /home/glot/src  USER glot WORKDIR /home/glot/  # generate certificate RUN go run $GOROOT/src/crypto/tls/generate_cert.go --host localhost  EXPOSE 8080  # CMD ["/home/glot/runner"] ENTRYPOINT ["/home/glot/server"]
    通过Dockerfile我们可以看出该项目主要的内容:利用Glot实现代码运行,产生证书用于https,利用echo实现web 服务。
    Web服务的代码如下:
    package code_runner  import ( 	"fmt" 	"github.com/prasmussen/glot-code-runner/cmd" 	"github.com/prasmussen/glot-code-runner/language" 	"io/ioutil" 	"os" 	"path/filepath" )  type Payload struct { 	Language string          `json:"language"` 	Files    []*InMemoryFile `json:"files"` 	Stdin    string          `json:"stdin"` 	Command  string          `json:"command"` }  type InMemoryFile struct { 	Name    string `json:"name"` 	Content string `json:"content"` }  type Result struct { 	Stdout string `json:"stdout"` 	Stderr string `json:"stderr"` 	Error  string `json:"error"` }  func Run(payload *Payload) *Result { 	// Ensure that we have at least one file 	if len(payload.Files) == 0 { 		exitF("No files given\n") 	}  	// Check if we support given language 	if !language.IsSupported(payload.Language) { 		exitF("Language '%s' is not supported\n", payload.Language) 	}  	// Write files to disk 	filepaths, err := writeFiles(payload.Files) 	if err != nil { 		exitF("Failed to write file to disk (%s)", err.Error()) 	}  	var stdout, stderr string  	// Execute the given command or run the code with 	// the language runner if no command is given 	if payload.Command == "" { 		stdout, stderr, err = language.Run(payload.Language, filepaths, payload.Stdin) 	} else { 		workDir := filepath.Dir(filepaths[0]) 		stdout, stderr, err = cmd.RunBashStdin(workDir, payload.Command, payload.Stdin) 	}  	result := &Result{ 		Stdout: stdout, 		Stderr: stderr, 		Error:  errToStr(err), 	}  	return result }  // Writes files to disk, returns list of absolute filepaths func writeFiles(files []*InMemoryFile) ([]string, error) { 	// Create temp dir 	tmpPath, err := ioutil.TempDir("", "") 	if err != nil { 		return nil, err 	}  	paths := make([]string, len(files), len(files)) 	for i, file := range files { 		path, err := writeFile(tmpPath, file) 		if err != nil { 			return nil, err 		}  		paths[i] = path  	} 	return paths, nil }  // Writes a single file to disk func writeFile(basePath string, file *InMemoryFile) (string, error) { 	// Get absolute path to file inside basePath 	absPath := filepath.Join(basePath, file.Name)  	// Create all parent dirs 	err := os.MkdirAll(filepath.Dir(absPath), 0775) 	if err != nil { 		return "", err 	}  	// Write file to disk 	err = ioutil.WriteFile(absPath, []byte(file.Content), 0664) 	if err != nil { 		return "", err 	}  	// Return absolute path to file 	return absPath, nil }  func exitF(format string, a ...interface{}) { 	fmt.Fprintf(os.Stderr, format, a...) 	os.Exit(1) }  func errToStr(err error) string { 	if err != nil { 		return err.Error() 	}  	return "" }

    可以通过容器方便的启动该服务,然后就可以通过Rest请求,执行Python(Golang)的代码。
  • curl \   -X POST \   http://localhost:8080/run \   -H 'Content-Type: application/json' \   -d '{"language":"python","files":[{"name":"main.py","content":"print(42)"}]}'  {"stdout":"42\n","stderr":"","error":""}

    在这个项目中,我用了echo来实现一个轻量级的Web服务,而没有使用Glot自带的基于Ruby的服务,这样做的好处是技术栈的统一,因为echo和glot的核心都是用的Golang。

  • Code Runner Web https://github.com/gangtao/code_runner_web
    有了服务,下面就是前端的UI了。
    UI使用了codemirror来做编辑器。Dockerfile如下:
    FROM naughtytao/code_runner  MAINTAINER gangtao@outlook.com  Add ./static /home/glot/static

    运行Code Runner Web后,就可以在以下的web界面中输入你想要运行的结果,并实时的显示想要运行的结果了。

好了,剩下的还有一些事情要做,就是准备你的演示代码,大家可以参考这个项目,这里缺省是将所有的代码片段放在code/python/ 目录下。运行:http://localhost:8080/#2 就会加载第二个代码片段。

这个是嵌入后的效果:

好了,是不是很Coooooool呢?

另外利用容器来构建应用真的非常非常方便。

本文发表于2017年09月07日 11:56
(c)注:本文转载自https://my.oschina.net/taogang/blog/1530258,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2066 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1