# VPython進階教學:按鈕 > 作者:王一哲 > 第1版:2018/7/23 > 第2版:2021/8/22 <br /> 由於我們之前做的動畫,都是在按在 F5 後自動開始執行,如果我們想要在動畫中新增按鈕,讓使用者可以自己控制動畫,應該要怎麼做呢?我們希望按鈕的功能有: 1. **執行**:按下時開始執行動畫,按鈕上的文字變為 **暫停**;再按一下暫停動畫,按鈕上的文字變為 **執行**。 2. **重置**:按下時重置木塊的位置 3. 用滑桿控制木塊的速度 我們使用最簡單的動畫〈[等速度直線運動](https://hackmd.io/@yizhewang/HJ7Ejj-GX)〉的程式碼來改寫,這是[線上版連結](https://www.glowscript.org/#/user/yizhe/folder/Public/program/APButton),成果如下: <iframe src="https://www.glowscript.org/#/user/yizhe/folder/Public/program/APButton" width="800" height="1400"></iframe> ## 程式碼 ```python= from vpython import * Web VPython 3.2 """ 1. 參數設定, 設定變數及初始值 """ d = 10 # 木塊邊長 L = 200 # 地板長度 t = 0 # 時間 dt = 0.01 # 時間間隔 re = False # 重置狀態 running = False # 物體運動狀態 """ 2. 畫面及函式設定 """ # 初始畫面設定 scene = canvas(width=800, height=400, x=0, y=0, background=vec(0, 0.6, 0.6), center=vec(0, 5, 0)) scene.title = "1D Motion\n\n" floor = box(pos=vec(0, 0, 0), size=vec(L, 0.1*d, 0.5*L), color=color.blue) cube = box(pos=vec(0, 0.55*d, 0), size=vec(d, d, d), v=vec(0, 0, 0), color=color.red) gd = graph(width=600, height=450, x=0, y=400, xtitle="<i>t</i> (s)", ytitle="<i>x</i> (cm)", fast=False) gd2 = graph(width=600, height=450, x=0, y=850, xtitle="<i>t</i> (s)", ytitle="<i>v</i> (cm/s)", ymin=-6.0, ymax=6.0, fast=False) xt = gcurve(graph=gd, color=color.red) vt = gcurve(graph=gd2, color=color.red) # 執行按鈕 def run(b1): global running running = not running if running: b1.text = "暫停" else: b1.text = "執行" b1 = button(text="執行", pos=scene.title_anchor, bind=run) # 重置按鈕 def reset(b2): global re re = not re b2 = button(text="重置", pos=scene.title_anchor, bind=reset) # 設定速度滑桿 def setv(vslider): cube.v.x = vslider.value vtext.text = "{:1.1f} cm/s".format(vslider.value) vslider = slider(min=-5.0, max=5.0, value=1.0, length=200, bind=setv, right=15, pos=scene.title_anchor) vtext = wtext(text="{:1.1f} cm/s".format(vslider.value), pos=scene.title_anchor) cube.v.x = vslider.value # 重置用, 初始化 def init(): global re, running, t cube.pos = vec(0, 0.55*d, 0) cube.v.x = vslider.value t = 0 xt.delete() vt.delete() re = False running = False b1.text = "執行" # 更新狀態 def update(): global t cube.pos += cube.v*dt xt.plot(pos=(t, cube.pos.x)) vt.plot(pos=(t, cube.v.x)) t += dt """ 3. 主程式 """ while True: rate(1000) if (cube.pos.x <= -L*0.5+d*0.5 and cube.v.x < 0) or (cube.pos.x >= L*0.5-d*0.5 and cube.v.x > 0): cube.v.x = 0 if running: update() if re: init() ``` <br /> ### 參數設定 除了一些物件的資料之外,另外設定了**重置狀態re**、**物體運動狀態running**等2個變數,預設值皆為False。 ### 畫面及函式設定 1. 先用以下的程式碼新增動畫視窗、木塊、地板。 ```python scene = canvas(width=800, height=400, x=0, y=0, background=vec(0, 0.6, 0.6), center=vec(0, 5, 0)) scene.title = "1D Motion\n\n" floor = box(pos=vec(0, 0, 0), size=vec(L, 0.1*d, 0.5*L), color=color.blue) cube = box(pos=vec(0, 0.55*d, 0), size=vec(d, d, d), v=vec(0, 0, 0), color=color.red) ``` 2. 設定執行按鈕**b1**,分為2個部分: ```python def run(b1): global running running = not running if running: b1.text = "暫停" else: b1.text = "執行" b1 = button(text="執行", pos=scene.title_anchor, bind=run) ``` (1) 自訂函式**run(b1)**,函式內使用了全域變數**running**,當b1被按下時,將running的狀態反過來,若原為False則改為True,若原為True則改為False;改變按鈕上的文字,若running的狀態為True則文字改為**暫停**,若running的狀態為False則文字改為**執行**。 (2) 用**button**產生按鈕b1,其中**text**為按鈕上顯示的文字,**pos**為按鈕位置,在此設定為scene的標題位置(scene.title_anchot),**bind**則是用來將這個按鈕物件連結到自訂函式run。 3. 設定重置按鈕**b2**,分為3個部分: ```python def reset(b2): global re re = not re b2 = button(text="重置", pos=scene.title_anchor, bind=reset) ``` ```python def init(): global re, running cube.pos = vec(0, size*0.55, 0) cube.v.x = vslider.value t = 0 xt.delete() vt.delete() re = False running = False b1.text = "執行" ``` (1) 自訂函式**reset(b2)**,函式內使用了全域變數**re**,當b2被按下時,將re的狀態反過來,若原為False則改為True,若原為True則改為False。 (2) 用**button**產生按鈕b2並連結到自訂函式reset。 (3) 自訂函式**init()**,執行此函式時,木塊會回到初位置,清除x-t圖、v-t圖中的資料,將running設定為False,將按鈕b1的文字變為**執行**。 4. 設定控制速度的滑桿,分為4個部分: ```python def setv(vslider): cube.v.x = vslider.value vtext.text = '{:1.1f} cm/s'.format(vslider.value) vslider = slider(min=-5.0, max=5.0, value=1.0, length=200, bind=setv, right=15, pos=scene.title_anchor) vtext = wtext(text='{:1.1f} cm/s'.format(vslider.value), pos=scene.title_anchor) cube.v.x = vslider.value ``` (1) 自訂函式**setv(vslider)**,修改木塊的速度、滑桿右側顯示速度的文字。 (2) 用**slider**產生滑桿vslider並連結到自訂函式setv。 (3) 用**wtext**產生顯示速度的文字vtext。 (4) 設定木塊的初速度。 6. 自訂函式**update()**,執行此函式時,更新木塊的位置,繪製x-t圖、v-t圖,更新時間。 <br /> ### 主程式 由於我們將大部分的內容及效果寫在自訂函式中,所以主程式非常短,只有以下幾行 ```python while True: rate(1000) if (cube.pos.x <= -L*0.5+size*0.5 and cube.v.x < 0) or (cube.pos.x >= L*0.5-size*0.5 and cube.v.x > 0): cube.v.x = 0 if running: update() if re: init() ``` **注意事項:rate(1000) 一定要放在 while 迴圈裡面,否則動畫無法順利執行。** <br /> 1. 如果木塊碰到地板左側邊緣且速度向左,或是木塊碰到地板右側邊緣且速度向右,將速度速度設定為0。 2. 如果running的值為True,呼叫自訂函式update()更新狀態。 3. 如果re的值為True,呼叫自訂函式init()。 <br /> ## 執行效果 我們可以用滑桿控制木塊的速度使木塊來回移動,當木塊抵達地板邊緣時,木塊會停止移動,但是時間仍會繼續增加,這是和第1版程式最大的差異。 <img style="display: block; margin-left: auto; margin-right: auto" height="60%" width="60%" src="https://i.imgur.com/GKfxaIJ.png"> <div style="text-align:center">用滑桿控制木塊來回移動的x-t圖</div> <br /> ## 結語 用VPython製作一個有操作界面的動畫,雖然也能做出不錯的效果,但是我們需要花較多的時間寫程式,用來處理物理模型的時間相對上較少,所以我們只簡單地做了這個動畫,之後的課程還是以處理物理模型為主。如果對於VPython的按鈕、滑桿、選單、核取方塊……等元件的用法有興趣,請參考GlowScript網站上的範例自行改寫。 <br /> ## 參考資料 1. VPython官方說明書**Widgets**: http://www.glowscript.org/docs/VPythonDocs/controls.html 2. GlowScript網站範例**Buttons Sliders Menus**: http://www.glowscript.org/#/user/GlowScriptDemos/folder/Examples/program/ButtonsSlidersMenus-VPython --- ###### tags: `VPython`