對於初學者而言, 在瀏覽器中直接學習 Python 有許多好處. 使用者無需安裝解譯系統, 甚至也不需要近端的編輯器, 直接透過瀏覽器上網後, 就可以開始練習.
以雙連桿的簡單平面機械手臂逆運動學方程式運算為例, 使用者一旦了解如何推導 IK 方程式, 就可以直接到 網際 Python 程式區 執行測試.
Github Gist
根據 Github Gist 的說明, 每一個 Gist 都是倉儲, 可以 fork 與 clone, 也可以分為 public 或 private, 當使用者在各自的帳號下建立 Gist 後, 可以透過 url 擷取, 且每一個不同版本各有一個版次號.
例如:
two_link_ik.py 目前共有兩個版次號, 也就是 two_link_ik.py 舊版 以及 two_link_ik.py 新版.
至於在 網際 Python 程式區中, 可以將此 Gist url 所對應的程式碼, 設定為 button 按鈕, 使用者一旦按下某一按鈕, Brython 就會將程式碼放入 editor 中, 讓使用者按下 run 按鈕後執行.
Brython 程式配置
ace.py 包含 traceback.py, 參考來源為 editor.py.
import sys import time import traceback import javascript from browser import document as doc, window, alert if hasattr(window, 'localStorage'): from browser.local_storage import storage else: storage = None class cOutput: def __init__(self, target): self.target = doc[target] def write(self, data): self.target.value += str(data) class Editor(): def __init__(self, editor_id, console_id, container_id, storage_id): self.editor_id = editor_id self.console_id = console_id self.container_id = container_id self.storage_id = storage_id self.output = '' try: self.editor = window.ace.edit(self.editor_id) session = self.editor.getSession() session.setMode("ace/mode/python") self.editor.setOptions({ 'enableLiveAutocompletion': True, 'enableSnippets': True, 'highlightActiveLine': False, 'highlightSelectedWord': True, 'autoScrollEditorIntoView': True, # 'maxLines': session.getLength() 可以根據程式長度設定 editor 列數 # 設定讓使用者最多可以在畫面中顯示 20 行程式碼 'maxLines': 20, 'fontSize': '12pt' }) except: from browser import html self.editor = html.TEXTAREA(rows=20, cols=70) doc[self.editor_id] <= self.editor def get_value(): return self.editor.value def set_value(x): self.editor.value = x self.editor.getValue = get_value self.editor.setValue = set_value def run(self, *args): sys.stdout = cOutput(self.console_id) sys.stderr = cOutput(self.console_id) doc[self.console_id].value = '' src = self.editor.getValue() if storage is not None: storage[self.storage_id] = src t0 = time.perf_counter() try: #ns = {'__name__':'__main__'} # 以 self.editor_id 名稱執行程式 #ns = {'__name__': self.editor_id} # execute program under the __main__ name ns = {'__name__':'__main__'} exec(src, ns) state = 1 except Exception as exc: traceback.print_exc(file=sys.stderr) state = 0 self.output = doc[self.console_id].value print('' % ((time.perf_counter() - t0) * 1000.0)) return state def show_console(self, ev): doc[self.console_id].value = self.output doc[self.console_id].cols = 60 doc[self.console_id].rows = 10 def clear_console(self, ev): doc[self.console_id].value = "" def clear_container(self, ev): doc[self.container_id].clear() # load a Python script def load_script(self, evt): _name = evt.target.value + '?foo=%s' % time.time() self.editor.setValue(open(_name).read())
beditor.py
import ace import json from browser import document as doc class gist(): """gist class to get program from Python gist""" def __init__(self, id, filename): self.id = id self.filename = filename def get_file(self): url = 'https://api.github.com/gists/' + self.id # info is string info = open(url).read() gist = json.loads(info) return gist["files"][self.filename]["content"] class editor(): """ace brython editor""" def __init__(self, script, editor_id, console_id, container_id, storage_id): self.script = script self.editor_id = editor_id self.console_id = console_id self.container_id = container_id self.storage_id = storage_id self.Ace = ace.Editor(editor_id=self.editor_id , console_id=self.console_id , container_id=self.container_id , storage_id=self.storage_id) # 所有的 button 程式碼中, 選擇一個執行 setValue(), 可以顯示在對應 id 的程式編輯區 def setValue(self): self.Ace.editor.setValue(self.script) def prog(self, ev): '''Ace = ace.Editor(editor_id="kw_editor" , console_id="kw_console" , container_id="kw__container" , storage_id="kw_py_src" ) ''' ''' Ace = ace.Editor(editor_id=self.editor_id , console_id=self.console_id , container_id=self.container_id , storage_id=self.storage_id) ''' self.Ace.editor.setValue(self.script) self.Ace.editor.scrollToRow(0) self.Ace.editor.gotoLine(0) def run(self, ev): self.Ace.run(ev) def show_console(self, ev): self.Ace.show_console(ev) def clear_console(self, ev): self.Ace.clear_console(ev) def clear_container(self, ev): self.Ace.clear_container(ev)
程式引用
<h2>Gist</h2> <h4>以下讓使用者將 Python 範例程式存入 <a href="https://docs.github.com/en/github/writing-on-github/editing-and-sharing-content-with-gists/creating-gists">Github Gist</a> 後, 經由程式 pubic Gist URL 連結, 以按鈕放入編輯器中執行:</h4> <!-- 導入 brython 程式庫 --> <script src="/static/brython.js"></script> <script src="/static/brython_stdlib.js"></script> <!-- 啟動 Brython --> <script>// <![CDATA[ window.onload=function(){ brython({debug:1, pythonpath:['/static/','./../downloads/py/']}); } // ]]></script> <p><!-- 導入 FileSaver 與 filereader --></p> <p> <script type="text/javascript" src="/static/ace/FileSaver.min.js"></script> <script type="text/javascript" src="/static/ace/filereader.js"></script> </p> <p><!-- 導入 ace --></p> <p> <script type="text/javascript" src="/static/ace/ace.js"></script> <script type="text/javascript" src="/static/ace/ext-language_tools.js"></script> <script type="text/javascript" src="/static/ace/mode-python3.js"></script> <script type="text/javascript" src="/static/ace/snippets/python.js"></script> </p> <p><!-- 請注意, 這裡使用 Javascript 將 localStorage["py_src"] 中存在近端瀏覽器的程式碼, 由使用者決定存檔名稱--></p> <p> <script type="text/javascript">// <![CDATA[ function doSave(storage_id, filename){ var blob = new Blob([localStorage[storage_id]], {type: "text/plain;charset=utf-8"}); filename = document.getElementById(filename).value saveAs(blob, filename+".py"); } // ]]></script> </p> <p><!-- 印出版次與關鍵字程式 --></p> <p> <script type="text/python3">// <![CDATA[ from browser import document as doc from browser import html import ace # 清除畫布 def clear_bd(ev): bd = doc["brython_div"] bd.clear() # Brython 3.3.4 內建的 container 名稱為 'container' 且 turtle 輸出為 svg 必須使用 div 訂定 id Ace = ace.Editor(editor_id="kw_editor", console_id="kw_console", container_id="kw__container", storage_id="kw_py_src" ) # 從 gist 取出程式碼後, 放入 editor 作為 default 程式 url = "https://gist.githubusercontent.com/mdecourse/379f02862e9dfd95dbc5241d4faa2ad4/raw/e3dc77e68bbfb8f00eef2e78d3c8d2323b0f17da/clock.py" data = open(url).read() Ace.editor.setValue(data) Ace.editor.scrollToRow(0) Ace.editor.gotoLine(0) # 執行程式, 顯示輸出結果與清除輸出結果及對應按鈕綁定 doc['kw_run'].bind('click', Ace.run) doc['kw_show_console'].bind('click', Ace.show_console) doc['kw_clear_console'].bind('click', Ace.clear_console) doc['clear_bd'].bind('click', clear_bd) // ]]></script> </p> <p><!-- 用來顯示程式碼的 editor 區域 --></p> <div id="kw_editor" style="width: 600px; height: 300px;"></div> <p><!-- 以下的表單與按鈕與前面的 Javascript doSave 函式以及 FileSaver.min.js 互相配合 --></p> <!-- 存擋表單開始 --><form><label>Filename: <input id="kw_filename" placeholder="input file name" type="text">.py</label> <input onclick="doSave('kw_py_src', 'kw_filename');" type="submit" value="Save"></form><!-- 存擋表單結束 --> <p></p> <!-- 執行與清除按鈕開始 --> <p><button id="kw_run">Run</button> <button id="kw_show_console">Output</button> <button id="kw_clear_console">清除輸出區</button><button id="clear_bd">清除繪圖區</button><button onclick="window.location.reload()">Reload</button></p> <!-- 執行與清除按鈕結束 --> <p></p> <!-- 程式編輯檔案 --> <div style="width: 100%; height: 100%;"><textarea autocomplete="off" id="kw_console"></textarea></div> <!-- 程式輸入位置 --> <div id="brython_div"></div> <div class="col-md-0" height="1" id="graphics-column" width="1"></div> <!-- 開始建立程式範例以及對應按鈕 --> <p><button id="snake">Snake</button><button id="clock">Clock</button><button id="two_link_ik">Two link IK</button></p> <!-- clock 開始 --> <script type="text/python3">// <![CDATA[ # 導入 document 與 beditor from browser import document import beditor # 設定按鈕 id 名稱 name 與 原始碼對應 url name = "clock" url = "https://gist.githubusercontent.com/mdecourse/379f02862e9dfd95dbc5241d4faa2ad4/raw/e3dc77e68bbfb8f00eef2e78d3c8d2323b0f17da/clock.py" # 利用 open 與 read 取回原始碼 src = open(url).read() # 將原始碼放入 editor e = beditor.editor(src, "kw_editor", "kw_console", "kw__container", "kw_py_src") # 設定 id 為 name 的元件按下後會執行 e.prog, 意即將原始碼放入 editor 中 document[name].bind('click', e.prog) // ]]></script> <!-- clock 結束 --> <p><!-- snake 開始 --> <script type="text/python3">// <![CDATA[ from browser import document as doc # 導入位於 static 目錄下的 beditor.py import beditor # 利用 beditor.py 中的 editor 類別建立案例, 對應到 snake snake_url = "https://gist.githubusercontent.com/mdecourse/d306a1f57e53bfd6466eaae20bcb9439/raw/2160a12b9fec9707a120a383ed5d38b9b78a02cf/snake.py" snake_src = open(snake_url).read() snake = beditor.editor(snake_src, "kw_editor", "kw_console", "kw__container", "kw_py_src") # id 為 "snake" 的按鈕點按時, 執行 snake 物件中的 prog 方法 doc["snake"].bind('click', snake.prog) // ]]></script> </p> <p><!-- snake 結束--></p> <!-- two link robot ik starts --> <script type="text/python3">// <![CDATA[ # based on https://mde.tw/cd2021/content/W14-W15.html from browser import document as doc import beditor url = "https://gist.githubusercontent.com/mdecourse/b58d23e73ff57c9ab1334f2e01cdc6e0/raw/2a0393fff3f621bc0267f2102697e586f99b8282/two_link_ik.py" src = open(url).read() two_link_ik = beditor.editor(src, "kw_editor", "kw_console", "kw__container", "kw_py_src") doc["two_link_ik"].bind('click', two_link_ik.prog) // ]]></script> <!-- two link robot ik ends -->
No comments:
Post a Comment