« 2009年4月 | トップページ | 2009年6月 »

2009年5月

2009年5月17日 (日)

時計をつくる5

再び表示系の処理を追加します。

今度はThrought Clockの特徴的な機能、針や文字以外は透けるようにします。
これを実現するためにどうやるか結構悩みました。

TopLevelWindow#set_transparentとかWindow#set_window_style_flagなどイロイロいじってみましたがどうもよろしくない。もう一旦ビットマップに落としてWx::SplashScreenでSPLASH_NO_TIMEOUTにしてタイマーイベントででペイントしていけばよいかな、と考えていました。

ん?ビットマップといえばサンプルにRubyロゴの形になったウィンドウのデモがあったことを思い出してどうやっているのか調べてみました。結果、TopLevelWindow#set_shapeを使えば実現できそう、ということで今回はこれを使って表示部分を書き換えてみます。

続きを読む "時計をつくる5"

| | コメント (0) | トラックバック (0)

時計をつくる4

今回はシステムトレイに格納するように改良してみます。
このやり方はサンプルによい例があったのでこれをほぼ丸写しです。(^-^;
Wx::TaskBarIconというクラスがあるのでこれを継承して新しいクラスを作ります。

続きを読む "時計をつくる4"

| | コメント (0) | トラックバック (0)

2009年5月 9日 (土)

時計をつくる3

前回ジャギってしまった時針をWx::GraphicsContextのメソッドを使って
アンチエイリアスを効かせて描いてみます。

どうやらGCDCからGraphicsContextをgetして描画することが出来るようですが、ドキュメントにそのようなメソッドが書かれていないのでDCからGraphicsContextを生成して描くことにします。

GraphicsContextには便利そうなメソッドがいっぱいありそうです。これらを使って他の部分も書き換えてみます。

続きを読む "時計をつくる3"

| | コメント (0) | トラックバック (0)

時計をつくる2

前回は単純な線だった時計の時針と分針をもう少し時計らしい針にしてみます。
draw_polygonというメソッドを使えばよいらしい。
とりあえず針を描くだけでまだ時間は刻みません。

続きを読む "時計をつくる2"

| | コメント (0) | トラックバック (0)

2009年5月 3日 (日)

時計をつくる1

ちょっと突然ですが、ラーメンタイマーを作っていて、これデジタル時計っぽいなと、ふと思った。
普段ハマたろう氏のThrough Clockというアプリケーションを使って時刻を表示しているんですが、職場のPCが何故かVistaにリプレースされる予定で、このアプリがVistaでも動くのかどうか確認しようとしたところ、すっかり更新が止まっているようで作者様のHPも閉鎖されているようです。

そのままコピーすれば動きそうな気がするんですが、今後のアップグレードにも対応できるかどうか不明なので、ネタにもなるし「Through Clockの様な」時刻表示するアプリケーションを作ってみることにする。

とりあえず実現したい機能をまとめてみた。

  • アナログで時刻表示
  • 邪魔にならないように透過表示
  • マウスと重なったら逃げる
  • 「右上」とかの指定でポジション指定
  • 「常に手前」のオン・オフ

時間があったら追加したい機能。

  • 針の表示の仕方など細かく指定
  • NTPで時刻合わせ

個人的に使うので表示は好みのモノでとりあえずつくる予定。アラームとか使用していないので今のところ不要。
目標が決まったので、必要な機能を洗い出してみる。とりあえずこんな所だろうか。

  • 時刻の取得
  • アナログ時計の描画
  • 描画更新処理
  • 透過表示処理
  • マウスとの重なり判定
  • ディスプレイサイズの取得
  • タスクバーに登録
  • 設定の保存

一つ一つ、どうやればよいのか調べていってみよう。
まず、時刻の取得。これはTimeクラスというそのままのクラスがあった。Time.nowでその時点の時刻が取得できる。とりあえずはこれを使おう。

次にアナログ時計の描画。こういう処理はやったことないので、wxRubyで始めるGUIプログラミングさんの落書きアプリを参考にさせてもらった。paintメソッドで渡されたDevice Contextをつかって円や線を引けばよいらしい。

......
paint do |dc|
  dc.draw_circle(dc.size.width / 2, dc.size.height / 2, dc.size.height / 2)
......

あとは、本物時計のように、時間に沿って時針や分針を描けばよい。針を描くに当たって、どうやって針の座標をきめるのか。ここで学生時代に習った三角関数が役に立つ。授業中は何の役に立つやらサッパリわからんかったが、Wikipediaの説明で理解できた。ただ机上で理解したつもりでも、実際やってみると動きがおかしかったのでとりあえず正しく表示できるかどうか確認するアプリを作ってみた。

XRCファイルはコレ。

  1. <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
  2. <resource xmlns="http://www.wxwindows.org/wxxrc" version="2.3.0.1">
  3.   <object class="wxFrame" name="MyFrame1" subclass="RTC">
  4.     <style>wxDEFAULT_FRAME_STYLE|wxSTAY_ON_TOP|wxTAB_TRAVERSAL</style>
  5.     <size>500,300</size>
  6.     <title></title>
  7.     <object class="wxBoxSizer">
  8.       <orient>wxHORIZONTAL</orient>
  9.       <object class="sizeritem">
  10.         <option>0</option>
  11.         <flag>wxALL</flag>
  12.         <border>5</border>
  13.         <object class="wxStaticText" name="m_staticText1">
  14.           <label>Hour</label>
  15.         </object>
  16.       </object>
  17.       <object class="sizeritem">
  18.         <option>0</option>
  19.         <flag>wxALL</flag>
  20.         <border>5</border>
  21.         <object class="wxSpinCtrl" name="m_spinCtrl1">
  22.           <style>wxSP_ARROW_KEYS</style>
  23.           <value>0</value>
  24.           <min>0</min>
  25.           <max>23</max>
  26.         </object>
  27.       </object>
  28.       <object class="sizeritem">
  29.         <option>0</option>
  30.         <flag>wxALL</flag>
  31.         <border>5</border>
  32.         <object class="wxStaticText" name="m_staticText2">
  33.           <label>Minute</label>
  34.         </object>
  35.       </object>
  36.       <object class="sizeritem">
  37.         <option>0</option>
  38.         <flag>wxALL</flag>
  39.         <border>5</border>
  40.         <object class="wxSpinCtrl" name="m_spinCtrl2">
  41.           <style>wxSP_ARROW_KEYS</style>
  42.           <value>0</value>
  43.           <min>0</min>
  44.           <max>59</max>
  45.         </object>
  46.       </object>
  47.     </object>
  48.   </object>
  49. </resource>
  50.  

検証なので必要最低限のモノだけ入れてます。これを変換して、

  1.  
  2. # This class was automatically generated from XRC source. It is not
  3. # recommended that this file is edited directly; instead, inherit from
  4. # this class and extend its behaviour there. 
  5. #
  6. # Source file: noname.xrc
  7. # Generated at: Sun May 03 17:00:57 &#26481;&#20140; (&#27161;&#28310;&#26178;) 2009
  8.  
  9. class RTC < Wx::Frame
  10.  
  11.   attr_reader :m_statictext1, :m_spinctrl1, :m_statictext2,
  12.               :m_spinctrl2
  13.  
  14.   def initialize(parent = nil)
  15.     super()
  16.     xml = Wx::XmlResource.get
  17.     xml.flags = 2 # Wx::XRC_NO_SUBCLASSING
  18.     xml.init_all_handlers
  19.     xml.load("noname.xrc")
  20.     xml.load_frame_subclass(self, parent, "MyFrame1")
  21.  
  22.     finder = lambda do | x |
  23.       int_id = Wx::xrcid(x)
  24.       begin
  25.         Wx::Window.find_window_by_id(int_id, self) || int_id
  26.       # Temporary hack to work around regression in 1.9.2; remove
  27.       # begin/rescue clause in later versions
  28.       rescue RuntimeError
  29.         int_id
  30.       end
  31.     end
  32.  
  33.     @m_statictext1 = finder.call("m_staticText1")
  34.     @m_spinctrl1 = finder.call("m_spinCtrl1")
  35.     @m_statictext2 = finder.call("m_staticText2")
  36.     @m_spinctrl2 = finder.call("m_spinCtrl2")
  37.     if self.class.method_defined? "on_init"
  38.       self.on_init()
  39.     end
  40.   end
  41. end
  42.  
  43.  

描画部分の実装。

  1. require 'rubygems'
  2. require 'wx'
  3. require 'RTC.rb'
  4.  
  5. class RubyThroughClock < RTC
  6.   def initialize(parent = nil)
  7.     super()
  8.  
  9.     evt_paint(){drawClock()}
  10.     m_spinctrl1.evt_spinctrl(m_spinctrl1.get_id()){|event| updateHour(event)}
  11.     m_spinctrl2.evt_spinctrl(m_spinctrl2.get_id()){|event| updateMin(event)}
  12.  
  13.     # structure for testing
  14.     @time = Struct.new('Time', :hour, :min).new
  15.     @time.hour = 0
  16.     @time.min = 0
  17.   end
  18.  
  19.   def updateHour(event)
  20.     @time.hour = @m_spinctrl1.get_value
  21.     drawClock()
  22.   end
  23.  
  24.   def updateMin(event)
  25.     @time.min = @m_spinctrl2.get_value
  26.     drawClock()
  27.   end
  28.  
  29.   def drawClock()
  30.     paint do |dc|
  31.     #paint_buffered do |dc|
  32.  
  33.       # Set background color
  34.       dc.set_background_mode(Wx::TRANSPARENT)
  35.       dc.set_background(Wx::NULL_BRUSH)
  36.  
  37.       # Clear DC
  38.       dc.set_brush(Wx::Brush.new(Wx::TRANSPARENT_BRUSH))
  39.       dc.clear
  40.  
  41.       # Get current time
  42.       #time = Time.now
  43.       time = @time
  44.  
  45.       # Draw the current time
  46.       dc.set_pen(Wx::Pen.new(Wx::RED, 8))
  47.       time_str = "%02d" % time.hour.to_s + ':' + "%02d" % time.min.to_s
  48.       dc.draw_text(time_str, 5, dc.size.height - 30)
  49.  
  50.       # Calculate hour's x,y
  51.       hour_x = - (dc.size.height / 2) * Math.sin(Math::PI * ( - 30 * (time.hour % 12)) / 180)  + (dc.size.width  / 2)
  52.       hour_y = - (dc.size.height / 2) * Math.cos(Math::PI * ( - 30 * (time.hour % 12)) / 180)  + (dc.size.height / 2)
  53.  
  54.       # Calculate min's x,y
  55.       min_x = - (dc.size.height / 2) * Math.sin(Math::PI * (- 6 * time.min ) / 180)  + (dc.size.width  / 2)
  56.       min_y = - (dc.size.height / 2) * Math.cos(Math::PI * (- 6 * time.min ) / 180)  + (dc.size.height / 2)
  57.  
  58.       # Draw Circle
  59.       dc.draw_circle(dc.size.width / 2, dc.size.height / 2, dc.size.height / 2)
  60.  
  61.       # Draw Hour hand
  62.       dc.set_pen(Wx::Pen.new(Wx::WHITE, 8))
  63.       dc.draw_line(dc.size.width / 2, dc.size.height / 2, hour_x.to_i, hour_y.to_i)
  64.  
  65.       # Draw Min hand
  66.       dc.set_pen(Wx::Pen.new(Wx::GREEN, 8))
  67.       dc.draw_line(dc.size.width / 2, dc.size.height / 2, min_x.to_i, min_y.to_i)
  68.  
  69.     end
  70.   end
  71. end
  72.  
  73.  
  74. class App < Wx::App
  75.   def on_init
  76.     f =  ::RubyThroughClock.new
  77.     f.show
  78.   end
  79. end
  80. App.new.main_loop
  81.  

起動すると、縮こまって表示されるので適当にリサイズして確認してみる。スピンボックスで時間と分を指定。こんな感じでよいのだろうか。三角関数に結構時間とられた。

01

| | コメント (0) | トラックバック (0)

« 2009年4月 | トップページ | 2009年6月 »