経過分秒表示(続々)、カウントダウン

http://d.hatena.ne.jp/hs9587/20100719/1279533327の続きというか、ライトニングトークを意識するならやっぱりカウントダウンが良いみたい。ので、そのように改造
LT_timer.rb

#coding:Windows-31J
# $Id: LT_timer.rb 3590 2010-12-30 07:00:56Z hs9587 $
# $Date: 2010-12-30 16:00:56 +0900 (譛ィ, 30 12 2010) $
# $Name$ $Author: hs9587 $ $Revision: 3590 $
Version = "0.0 ($Revision: 3590 $)"
require 'vr/vruby'
require 'vr/vrcontrol'
require 'vr/vrtimer'
require 'vr/vrlayout'
require 'time'
Min = Time.parse('00:01')-Time.parse('00:00')

class TimerControl < VRPanel
  include VRTimerFeasible
  include VRDrawable
  SrartResetStruct= Struct.new :name, :button, :body
  StartReset = [
    SrartResetStruct.new('start', '!' \
      , lambda{@start_time = Time.now; def anchor_time; @start_time; end}),
    SrartResetStruct.new('reset', '0' \
      , lambda{def anchor_time; Time.now; end}),
    SrartResetStruct.new('close', '.', lambda{exit}),
    ]
  attr_writer :time_out, :time_color, :countdown
  
  def construct
    self.caption = 'LTTimer'
    #addTimer 1000
    addTimer 50
    @font = @screen.factory.newfont 'MS ゴシック', -72
    #@font = @screen.factory.newfont 'HG ゴシックE', -72
    #@font = @screen.factory.newfont 'HGS ゴシックE', -72
    #@font = @screen.factory.newfont 'Tahoma', -72, 400, 0, 0, 0, 34, 0
    #@font = @screen.factory.newfont 'Tahoma', -72
    @time_out = 5.0
    @time_color = 3.0
  end # def construct
  
  def self_paint
    elapse = (Time.now - anchor_time).abs
    self.textColor = RGB elapsed(elapse), 0, 0
    setFont @font
    mm_ss = mm_ss elapse
    #p textExtent(mm_ss)
    drawText mm_ss, 0,0, *textExtent(mm_ss)
  end # def self_paint
  
  def self_timer
    refresh
  end
  
  StartReset.each do |start_reset|
    define_method start_reset.name, start_reset.body
  end # StartReset.each do |start_reset|
  
  private
  def anchor_time
    Time.now
  end # def anchor_time
  
  def elapsed(sec)
    case sec
      when 0 ... Min*@time_color then
        0
      when Min*@time_color ... Min*@time_out then
        256 * (sec - Min*@time_color) / (Min*(@time_out - @time_color))
      else
        sec < 0 ? 0 : 255
    end # case sec
  end # def elapsed(sec)
  
  def mm_ss(elapse)
    if @countdown then
      Time.at((@time_out*Min - elapse).floor.abs).strftime '%M:%S'
    else# if @countdown
      Time.at(elapse).strftime '%M:%S'
    end # if @countdown
  end # def mm_ss(elapse)
end # class TimerControl < VRPanel

class StartResetControl < VRPanel
  include VRVertLayoutManager
  include VRStdControlContainer
  
  def construct
    TimerControl::StartReset.each do |start_reset|
      addControl VRButton, start_reset.name, start_reset.button
      send_parent start_reset.name, 'clicked'
    end # TimerControl::StartReset.each do |start_reset|
  end # def construct
end # class StartResetControl < VRPanel

module LTTimer
  attr_reader :timer
  def construct
    self.caption = 'LTTimer'
    move 0,0, 198,104
    addControl TimerControl, 'timer', 'Timer', 0,0, 180,72
    addControl StartResetControl, 'buttons', 'Buttons', 181,0, 10,72
    top 0xffffffff
  end # def construct
  
  TimerControl::StartReset.each do |start_reset|
    define_method("buttons_#{start_reset.name}_clicked") do
      @timer.send start_reset.name
    end # define_method("buttons_#{start_reset.name}_clicked") do
  end # TimerControl::StartReset.each do |start_reset|
end # module LTTimer

time_out, time_color, countdown, start = 5, 3, false, false
require 'optparse'
ARGV.options do |opt|
  opt.on('-o', '--timeout=VAL', "default: #{time_out}(min)"){|v| time_out = v.to_f}
  opt.on('-c', '--timecolor=VAL', "default: #{time_color}(min)"){|v| time_color = v.to_f}
  opt.on('-d', '--[no-]countdown', "default: #{countdown}"){|v| countdown = v}
  opt.on('-s', '--[no-]start', "default: #{start}"){|v| start = v}
  opt.banner << "\nLeft upper topmost timer"
  begin # rescue OptionParser::ParseError => evar
    opt.parse!
  rescue OptionParser::ParseError => evar
    evar.display $stderr; "\n".display $stderr
    opt.help.display $stderr
    exit 1
  end # rescue OptionParser::ParseError => evar
end # ARGV.options do |opt|

#VRLocalScreen.start LTTimer
lt = VRLocalScreen.showForm LTTimer
lt.timer.time_out = time_out
lt.timer.time_color = time_color
lt.timer.countdown = countdown
lt.timer.start if start
VRLocalScreen.messageloop

カウントダウン用のオプションを定義した上で、大きな違いは表示分秒文字列をみる mm_ss(elapse)メソッドの所。

  def mm_ss(elapse)
    if @countdown then
      Time.at((@time_out*Min - elapse).floor.abs).strftime '%M:%S'
    else# if @countdown
      Time.at(elapse).strftime '%M:%S'
    end # if @countdown
  end # def mm_ss(elapse)

floor注意、こうしないと0秒の前後で 1秒ずれる。
Time.at() の処理は self_paintメソッドでの elapse計算時からこっちに移動した。一方表示色の方は基本的に elapsed()メソッドでの計算の儘、Time.at()処理の移動に伴い .to_i はいらなくなった。

オプションはカウントダウン側をデフォルト動作にしたものかどうか迷い中。

opt.on の事

オプション解析のところなんだけど

  opt.on('-s', '--[no-]start', "default: #{start}"){|v| start = v}

とか、同じキーワード(start)が 3回も出てくるのはちょっと気になる。

  {:s => :start}.each do |s_opt, l_opt|
    opt.on("-#{s_opt}", "--[no-]#{l_opt}", "default: #{eval l_opt}"){|v| eval "#{l_opt} = v"}
  end # {:s => :start}.each do |s_opt, l_opt|

とかするのだろうか。でもロングオプションの引数記法「=VAL」を使う方との切り替えはちょっと何だし、ここで eval はやり過ぎの様な気もする。そもそもオプション値のやりとりにローカル変数を使ってるから eval になるので、ハッシュとか別のコンテナを使えばいいのかも知れない、高が 4つのオプションにそれも大げさな気がする。
追求してると optparse の DSLを作るという話になってしまうのだろうか。儘でいいや。