Try   HackMD

VPython進階教學:按鈕

作者:王一哲
第1版:2018/7/23
第2版:2021/8/22


由於我們之前做的動畫,都是在按在 F5 後自動開始執行,如果我們想要在動畫中新增按鈕,讓使用者可以自己控制動畫,應該要怎麼做呢?我們希望按鈕的功能有:

  1. 執行:按下時開始執行動畫,按鈕上的文字變為 暫停;再按一下暫停動畫,按鈕上的文字變為 執行
  2. 重置:按下時重置木塊的位置
  3. 用滑桿控制木塊的速度

我們使用最簡單的動畫〈等速度直線運動〉的程式碼來改寫,這是線上版連結,成果如下:

程式碼

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()

參數設定

除了一些物件的資料之外,另外設定了重置狀態re物體運動狀態running等2個變數,預設值皆為False。

畫面及函式設定

  1. 先用以下的程式碼新增動畫視窗、木塊、地板。

    ​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個部分:

    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個部分:

    def reset(b2):
    ​​​​    global re
    ​​​​    re = not re
    ​​​​
    ​​​​b2 = button(text="重置", pos=scene.title_anchor, bind=reset)
    
    ​​​​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個部分:

    ​​​​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) 設定木塊的初速度。

  5. 自訂函式update(),執行此函式時,更新木塊的位置,繪製x-t圖、v-t圖,更新時間。


主程式

由於我們將大部分的內容及效果寫在自訂函式中,所以主程式非常短,只有以下幾行

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 迴圈裡面,否則動畫無法順利執行。


  1. 如果木塊碰到地板左側邊緣且速度向左,或是木塊碰到地板右側邊緣且速度向右,將速度速度設定為0。
  2. 如果running的值為True,呼叫自訂函式update()更新狀態。
  3. 如果re的值為True,呼叫自訂函式init()。

執行效果

我們可以用滑桿控制木塊的速度使木塊來回移動,當木塊抵達地板邊緣時,木塊會停止移動,但是時間仍會繼續增加,這是和第1版程式最大的差異。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

用滑桿控制木塊來回移動的x-t圖

結語

用VPython製作一個有操作界面的動畫,雖然也能做出不錯的效果,但是我們需要花較多的時間寫程式,用來處理物理模型的時間相對上較少,所以我們只簡單地做了這個動畫,之後的課程還是以處理物理模型為主。如果對於VPython的按鈕、滑桿、選單、核取方塊……等元件的用法有興趣,請參考GlowScript網站上的範例自行改寫。

參考資料

  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