對於初學者而言, 在瀏覽器中直接學習 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | 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 ( '<completed in="" %6.2f="" ms="">' % ((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()) < / completed> |
beditor.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | 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 ): # 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) |
程式引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | < 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 程式 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" # 利用 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_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 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