#

這一個章節我們要完成的功能稍微多一點,分別是音量控制、自動播放下一首及循環播放的功能。另外這個章節也會對核取方塊做一些簡單的介紹。

25.1 自動播放下一首

目前的iPlayer播放完當前的MP3,並不會自動播放下一首。這一小節我們要把這項功能加進去。在開始之前先來補充一個聲音物件的選項,請看下方的程式:

$snd play -command {::iPlayer::next}

其實聲音物件在執行play功能時,可以讓我們加上-command選項,放在這個選項之後的程式會在目前這首MP3播完時被執行。嗯~所以說,自動播放下一首的功能可以寫在這裡。其原理是目前這首MP3播完的時候,從播放清單找到下一首MP3來播放就可以了。現在先讓我們要先修改play及seek程序,讓聲音物件在播放完成時會執行next程序(等等就會實作了)。

#以下是修正後的play程序
proc play {} {
....以上的程式就不顯示了
 
 # 如果目前的狀態是 PAUSE
 if {$Priv(State) == "PAUSE"} {

  #========================================注意這邊!!
  # 繼續播放
  $snd play -command {::iPlayer::next}
  #========================================

  # 切換狀態為 PLAY
  set Priv(State) "PLAY"
  
  # 啟動timer
  ::iPlayer::timer
  
  return
 }
 
 ....這邊的程式就不顯示了
 
 # 載入新的MP3
 $snd configure -file $currentFile
 
 # 設定拉桿的 上/下 邊界值及目前值
 $Priv(prg) configure -value 0 -from 0 -to [$snd length -unit sec]

 #========================================注意這邊!!
 # 開始播放 
 $snd play -command {::iPlayer::next}
 #========================================

 # 切換狀態為 PLAY
 set Priv(State) "PLAY"
 
 # 啟動timer
 ::iPlayer::timer
}


#以下是seek修正後的程序
proc seek {} {
....以上的程式就不顯示了
 
 # 先停止播放
 $snd stop
 
 #========================================注意這邊!!
 # 由指定的位置(sample)播放
 # 由於-start只能接受整數,所以這邊用expr的int函數來確保$pos的值是整數
 $snd play -start [expr int ($pos)] -command {::iPlayer::next}
 #========================================
 
 # 重新啟動timer程序
 after 1000 ::iPlayer::timer  

}

大家還記得嗎? play程序會播放清單中被選中的項目。所以next程序的工作就是由清單中選取下一個項目,然後再重新執行play程序。

proc next {} {
 variable snd
 variable Priv  

 #先把目前的狀態切為STOP
 set Priv(State) "STOP"

 #取得目前播放清單被選中的項目
 set currItem [$Priv(pl) selection]

 #找出目前項目的下一個項目
 set nextItem [$Priv(pl) next $currItem]

 #如果下一個項目是空字串,就表示目前的項目已經是最後一個了
 if {$nextItem == ""} {return}

 #把下一個項目設定為選取中的狀態
 $Priv(pl) selection set $nextItem
 
 #1秒後開始播放下一首MP3 
 #注意哦!!1秒的延遲是必要的,因為我們要先讓timer自己停止,然後再重新播放
 after 1000 ::iPlayer::play
}

Ok!! 自動播放下一首的功能完成了,請大家自己試試吧!!

25.2 循環播放製作

循環播放指的是播完清單中的最後一首MP3之後,程式要可以重頭開始播放。這個功能我打算配合核取方塊這個視窗元件來完成,這樣一來可以順便介紹一下核取方塊。核取方塊的外觀如下:

圖 25-1


使用者可以在核取方塊上打勾表示選中某項東西。在這裡我打算讓使用者透過打勾來決定是否要循環播放清單。

核取方塊

核取方塊的建立及使用滿固定的,下方的程式大概就可以滿足大部分的應用:

set ::value 1
ttk::checkbutton .chk -text "打勾吧!!" -variable ::value -onvalue 1 -offvalue 0
pack .chk

上面的程式會建出1個核取方塊,預設它會被勾上。第1行建立了一個全域變數,我打算用它來儲存核取的狀態,也就是說使用者勾選或是沒勾選的狀態會立即反映在這個變數上。接下來是第2行,-text用來指定核取方塊後面要顯示的文字,-variable就如同entry的-textvariable選項一樣,其後必需放置一個變數的名稱,此變數會反映使用者的勾選狀態,-onvalue後指定的值會在方塊被勾選時儲存在::value變數裡,-offvalue後指定的值會在方塊未勾選時儲存在::value變數裡。

□ 制作循環播放核取方塊

現在我們可以幫iPlayer加上循環播放的核取方塊了,當然用來記錄方塊的核取狀態的變數也是不能少的,這幾項工作要在gui_init裡完成:

proc gui_init {} {
...以上的程式內容就不顯示了

 # 建立一個框架準備用來擺放拉桿
 set fme [::ttk::frame .fmePrg]
 pack $fme -expand 0 -fill x

 # 建立播放進度用的拉桿視窗元件 
 set prg [::ttk::scale $fme.sclPrg]
 pack $prg -expand 1 -fill both -side left

 #============================================注意這邊!!
 # 建立控制循環播放的核取方塊
 set Priv(loop) 0
 set chk [::ttk::checkbutton $fme.chkLoop -text "循環" -onvalue 1 -offvalue 0 -variable ::iPlayer::Priv(loop) ]
 pack $chk -fill x -side left
 #============================================

 # 把拉桿綁定「滑鼠左鍵放開事件」
 bind $prg <ButtonRelease-1> {::iPlayer::seek}
 
 # 把拉桿綁定「滑鼠左鍵下壓事件」
 bind $prg <ButtonPress-1> {after cancel ::iPlayer::timer }
 
 # 拉桿視窗元件在其它程序會被用到,所以記下它的path 
 set Priv(prg) $prg   

}

測試一下iPlayer應該已經加上核取方塊了:

圖 25-2


為了要讓循環播放能夠正確的執行,我們還要稍微對next程序加以修改:

proc next {} {
 variable snd
 variable Priv  

 #先把目前的狀態切為STOP
 set Priv(State) "STOP"

 #取得目前播放清單被選中的項目
 set currItem [$Priv(pl) selection]

 #找出目前項目的下一個項目
 set nextItem [$Priv(pl) next $currItem]

 #========================================================= 注意這邊!!
 #如果下一個項目是空字串,就表示目前的項目已經是最後一個了
 if {$nextItem == ""} {
  # 如果沒有勾選循環播放就離開程序
  if {$Priv(loop) == 0} {
   #離開前要先把播放進度的拉桿歸回原點
   $Priv(prg) configure -value 0
   return
  }
  
  # 取出清單方塊根項目下所有的子項目(即所有已加入的MP3檔案)
  set allItems [$Priv(pl) children {}]
  
  # 取出清單方塊中的第一個項目,設定為選取中的狀態
  $Priv(pl) selection set [lindex $allItems 0]
 } else {
  #把下一個項目設定為選取中的狀態
  $Priv(pl) selection set $nextItem
 }
 #========================================================= 

 #1秒後開始播放下一首MP3 
 #注意哦!!1秒的延遲是必要的,因為我們要先讓timer自己停止,然後再重新播放
 after 1000 ::iPlayer::play
}

修改的部份很單純,若$nextItem為空字串的話,表示目前已經播完清單中的最後一首,然後程式要判斷如果核取方塊沒有被勾選,就離開程序,否則的話往下執行,接下來的程式就是選第一首播放。好了,到目前為止循環播放的功能完成了,請自己試試吧!!

25.3 音量控制

用snack套件來控制音量是一件很簡單的事,下面是一個簡單的例子它會印出目前的音量百分比:

puts [::snack::audio play_gain]

如果要設定音量的話,可以像下面這樣做,這個例子會把音量大小調整為85%:

::snack::audio play_gain 85

加上音量控制器

了解音量控制的方法後,現在可以為iPlayer加上音量控制器了。如下請先在gui_init程序中建立一個滑桿元件來控制音量:

proc gui_init {} {
 variable Priv  

...以上的程式內容就不顯示了

 # 清除播放單清按鈕
 set btnPlClear [::ttk::button $fme.btnPlClear \
  -image [image create photo -file "pl_clear.gif"] \
  -command {::iPlayer::pl_clear} \
 ]
 pack $btnPlClear -side left    

 #================================================ 注意這邊!!
 # 建立控制音量的拉桿視窗元件
 set vol [::ttk::scale $fme.sclVol \
  -from 0 \
  -to 100 \
  -value [::snack::audio play_gain] \
  -command {::iPlayer::volume}]
 pack $vol -expand 1 -fill both -side left
 #================================================

 # 建立一個框架元件,把清單方塊及捲軸方塊放在裡面,以方便管理
 set fmePl [::ttk::frame .fmePl]
 pack $fmePl -expand 1 -fill both
 
 # 建立一個可以顯示清單的treeview視窗元件
 set pl [::ttk::treeview $fmePl.pl -show tree]


...以下的程式內容就不顯示了
}

在建立滑桿時我先把目前的音量設定給滑桿的-value選項當做拉桿的初始值。另外我也指定了滑桿的-command選項是::iPlayer::volume,意思是每次拉桿上的按鈕滑動時都會執行volume程序,而volume程序如下:

#控制音量的程序
proc volume {vol} {
 ::snack::audio play_gain [expr int($vol)]
}

由於play_gain只能接受整數值,所以我們要先用expr的int函數把$val的值轉為整數,再設定給play_gain。完成了請大家自己試試吧!!

圖 25-3


25.4 程式資源

在這邊可以下載到目前已完成的程式 - iPlayer.zip

Theme Design by devolux.org. Converted by Wordpress To Blogger for WP Blogger Themes. Sponsored by iBlogtoBlog
This template is brought to you by : allblogtools.com | Blogger Templates