« 時計をつくる3 | トップページ | 時計をつくる5 »

2009年5月17日 (日)

時計をつくる4

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

Wx::TaskBarIconを使った作製のポイントは、イベントIDとアイコンの作成、ポップアップメニュー表示になると思います。

まずイベントハンドラーで処理するためにinitialize部分でイベントIDを設定します。イベントIDを設定しないで処理するやり方もあると思いますが、多分設定した方が簡単に書けるとおもいます。

  1.   # Event ID
  2.   TBMENU_OPTION      = 6000
  3.   TBMENU_STAY_ON_TOP = 6001
  4.   TBMENU_MOVE_RU     = 6002
  5.   TBMENU_MOVE_RB     = 6003
  6.   TBMENU_MOVE_LU     = 6004
  7.   TBMENU_MOVE_LB     = 6005
  8.   TBMENU_EXIT        = 6006
  9.  
  10.   def initialize(frame)
  11.     super()
  12.  
  13.     @frame = frame
  14.     @stay_on_top = true
  15.     # starting image
  16.     icon = make_icon('ruby.png')
  17.     set_icon(icon, 'RubyThroughClock')
  18.  
  19.     # Menu Event
  20.     evt_menu(TBMENU_OPTION)      {|event| @frame.onTB_option(event) }
  21.     evt_menu(TBMENU_STAY_ON_TOP) {|event| onTB_stay_on_top(event) }
  22.     evt_menu(TBMENU_MOVE_RU)     {|event| @frame.onTB_moveRU(event) }
  23.     evt_menu(TBMENU_MOVE_RB)     {|event| @frame.onTB_moveRB(event) }
  24.     evt_menu(TBMENU_MOVE_LU)     {|event| @frame.onTB_moveLU(event) }
  25.     evt_menu(TBMENU_MOVE_LB)     {|event| @frame.onTB_moveLB(event) }
  26.     evt_menu(TBMENU_EXIT)        {|event| @frame.close }
  27.   end
  28.  

次にアイコン作成。コレはほぼ丸写しなので、プラットフォームごとに書き分けるにはこうやるのかと感心して読んでいるだけでした。システムトレイに入ったときに背景が黒くなってしまったので、set_maskだけ追加しました。Wx::Imageの原理がよく分かってないですがうまく透過したのでOKなのでしょう。

  1.   def make_icon(imgname)
  2.     # Different platforms have different requirements for the
  3.     #  taskbar icon size
  4.     filename = File.join( File.dirname(__FILE__), imgname )
  5.     img = Wx::Image.new(filename, Wx::BITMAP_TYPE_PNG)
  6.     img.set_mask
  7.     if Wx::PLATFORM == "WXMSW"
  8.       img = img.scale(16, 16)
  9.     elsif Wx::PLATFORM == "WXGTK"
  10.       img = img.scale(22, 22)
  11.     end
  12.     # WXMAC can be any size up to 128x128, so don't scale
  13.     icon = Wx::Icon.new
  14.     icon.copy_from_bitmap(Wx::Bitmap.new(img))
  15.     return icon
  16.   end
  17.  

最後にcreate_popup_menuメソッドをオーバーライドしてメニューを表示させるようにします。これもサンプルと同様にWx::Menuでポップアップメニューを作っておくやり方です。
"Stay On Top"の部分はオン・オフで状態が分かるようにcheckedにしてあります。通常のメニューだとMenuBarなのかどこなのか分かりませんが、自動的にこの状態を管理してくれるのですが、今回は都度生成しているので管理用のフラグを作製して状態を保存するようにしています。
実は、MenuBarへappendするやり方も試してみたのですがどうしてもメニューが表示されるようになってしまうのでこのやり方に落ち着きました。
メニュー項目は本家から自分が使いそうなモノを抜き出しています。位置関係はWx::Displayのメソッドでスクリーンの座標を調べて自分自身のサイズと計算して座標を決めています。マルチスクリーンはとりあえず考えてません。Optionはまだ未実装。

その他、文字盤の追加など入れたバージョンは下記です。

  1. begin
  2.   require 'rubygems'
  3. rescue LoadError
  4. end
  5. require 'wx'
  6. require 'RTC.rb'
  7.  
  8. class RubyThroughClock < RTC
  9.   def initialize(parent = nil)
  10.     super()
  11.  
  12.     @tbicon = RTC_TaskBarIcon.new(self)
  13.  
  14.     #m_menubar1.show(false)
  15.  
  16.     evt_paint(){drawClock()}
  17.     m_spinctrl1.evt_spinctrl(m_spinctrl1.get_id()){|event| updateHour(event)}
  18.     m_spinctrl2.evt_spinctrl(m_spinctrl2.get_id()){|event| updateMin(event)}
  19.     evt_close(){|event| onClose(event)}
  20.  
  21.     # structure for testing
  22.     @time = Struct.new('Time', :hour, :min).new
  23.     @time.hour = 0
  24.     @time.min = 0
  25.   end
  26.  
  27.   def onClose(event)
  28.     @tbicon.remove_icon
  29.     @tbicon.destroy
  30.     event.skip
  31.   end
  32.  
  33.   def updateHour(event)
  34.     @time.hour = @m_spinctrl1.get_value
  35.     drawClock()
  36.   end
  37.  
  38.   def updateMin(event)
  39.     @time.min = @m_spinctrl2.get_value
  40.     drawClock()
  41.   end
  42.  
  43.   def drawClock()
  44.     paint do |dc|
  45.     #paint_buffered do |dc|
  46.       # Create Graphics Context
  47.       gc = Wx::GraphicsContext.create(dc)
  48.  
  49.       # Set background color
  50.       dc.set_background_mode(Wx::TRANSPARENT)
  51.       dc.set_background(Wx::NULL_BRUSH)
  52.  
  53.       # Clear DC
  54.       dc.clear
  55.       # Get current time
  56.       #time = Time.now
  57.       time = @time
  58.  
  59.       # Draw the current time
  60.       time_str = "%02d" % time.hour.to_s + ':' + "%02d" % time.min.to_s
  61.       dc.draw_text(time_str, 5, dc.size.height - 30)
  62.  
  63.       # Offset X Y position
  64.       gc.translate(dc.size.width / 2, dc.size.height / 2)
  65.  
  66.       # Draw Circle
  67.       drawCircle(gc, dc.size.height / 2, Wx::Pen.new(Wx::RED, 3))
  68.  
  69.       # Draw Hour hand
  70.       hand = [ [0, dc.size.height / 30], [-5, 0], [0, -dc.size.height / 4], [5, 0] ]
  71.       drawHand(gc, hand, Wx::WHITE_BRUSH, Math::PI * ( 30 * (time.hour % 12)) / 180)
  72.  
  73.       # Draw Min hand
  74.       hand = [ [0, dc.size.height / 30], [-5, 0], [0, -dc.size.height / 2], [5, 0] ]
  75.       drawHand(gc, hand, Wx::BLUE_BRUSH, Math::PI * ( 6 * time.min ) / 180)
  76.  
  77.       end
  78.   end
  79.  
  80.   def drawHand(gc, hand, brush, radians)
  81.     path = gc.create_path
  82.     mt   = gc.get_transform
  83.  
  84.     # Set Hand Polygon
  85.     hand.each do |xy|
  86.       path.add_line_to_point(xy[0], xy[1])
  87.     end
  88.     path.close_subpath
  89.  
  90.     # Move Hand
  91.     gc.rotate(radians)
  92.  
  93.     # Pen Setting
  94.     gc.set_pen(Wx::Pen.new(Wx::BLACK, 1))
  95.  
  96.     # Brush Setting
  97.     gc.set_brush(brush)
  98.     gc.fill_path(path)
  99.     #gc.fill_path(path, Wx::WINDING_RULE)
  100.     #gc.stroke_path(path)
  101.     #gc.draw_path(path)
  102.  
  103.     gc.set_transform(mt)
  104.   end
  105.  
  106.   def drawCircle(gc, radius, pen)
  107.     path = gc.create_path
  108.     mt   = gc.get_transform
  109.  
  110.     # Font setting
  111.     font = Wx::NORMAL_FONT
  112.     font.set_point_size(14)
  113.     gc.set_font(font,Wx::BLACK)
  114.  
  115.     # Set Path
  116.     path.add_circle(0, 0, radius * 4 / 5 - font.get_point_size)
  117.     # Pen Setting
  118.     gc.set_pen(pen)
  119.     # Draw Circle
  120.     gc.draw_path(path)
  121.  
  122.     # Offset X Y position
  123.     gc.translate(-font.get_point_size / 2, -font.get_point_size / 2 - 5)
  124.     # Set the number of hour
  125.     gc.rotate(Math::PI)
  126.     for hour in 1..12
  127.       gc.rotate(Math::PI * 30  / 180)
  128.       gc.draw_text(hour.to_s, 0, radius * 4 / 5, Math::PI * (30 * hour + 180)  / 180)
  129.     end
  130.  
  131.     gc.set_transform(mt)
  132.   end
  133.  
  134.   def onTB_option(event)
  135.   end
  136.  
  137.   def onStay_on_top(event)
  138.     flag = get_window_style_flag
  139.     flag = flag - Wx::STAY_ON_TOP
  140.     set_window_style_flag(flag)
  141.     refresh
  142.   end
  143.  
  144.   def onTB_moveRU(event)
  145.     rect = Wx::Display.new.get_client_area
  146.     width = get_screen_rect.get_width
  147.     #height = get_screen_rect.get_height
  148.     point = rect.get_top_right
  149.     move(point.x - width, point.y)
  150.   end
  151.  
  152.   def onTB_moveRB(event)
  153.     rect = Wx::Display.new.get_client_area
  154.     width = get_screen_rect.get_width
  155.     height = get_screen_rect.get_height
  156.     point = rect.get_bottom_right
  157.     move(point.x - width, point.y - height)
  158.   end
  159.  
  160.   def onTB_moveLU(event)
  161.     rect = Wx::Display.new.get_client_area
  162.     move(rect.get_top_left)
  163.   end
  164.  
  165.   def onTB_moveLB(event)
  166.     rect = Wx::Display.new.get_client_area
  167.     #width = get_screen_rect.get_width
  168.     height = get_screen_rect.get_height
  169.     point = rect.get_bottom_left
  170.     move(point.x, point.y - height)
  171.   end
  172.  
  173. end
  174.  
  175. class RTC_TaskBarIcon < Wx::TaskBarIcon
  176.   # Event ID
  177.   TBMENU_OPTION      = 6000
  178.   TBMENU_STAY_ON_TOP = 6001
  179.   TBMENU_MOVE_RU     = 6002
  180.   TBMENU_MOVE_RB     = 6003
  181.   TBMENU_MOVE_LU     = 6004
  182.   TBMENU_MOVE_LB     = 6005
  183.   TBMENU_EXIT        = 6006
  184.  
  185.   def initialize(frame)
  186.     super()
  187.  
  188.     @frame = frame
  189.     @stay_on_top = true
  190.     # starting image
  191.     icon = make_icon('ruby.png')
  192.     set_icon(icon, 'RubyThroughClock')
  193.  
  194.     # Menu Event
  195.     evt_menu(TBMENU_OPTION)      {|event| @frame.onTB_option(event) }
  196.     evt_menu(TBMENU_STAY_ON_TOP) {|event| onTB_stay_on_top(event) }
  197.     evt_menu(TBMENU_MOVE_RU)     {|event| @frame.onTB_moveRU(event) }
  198.     evt_menu(TBMENU_MOVE_RB)     {|event| @frame.onTB_moveRB(event) }
  199.     evt_menu(TBMENU_MOVE_LU)     {|event| @frame.onTB_moveLU(event) }
  200.     evt_menu(TBMENU_MOVE_LB)     {|event| @frame.onTB_moveLB(event) }
  201.     evt_menu(TBMENU_EXIT)        {|event| @frame.close }
  202.   end
  203.  
  204.   def onTB_stay_on_top(event)
  205.     @stay_on_top = event.is_checked
  206.     @frame.onStay_on_top(event)
  207.   end
  208.  
  209.   def make_icon(imgname)
  210.     # Different platforms have different requirements for the
  211.     #  taskbar icon size
  212.     filename = File.join( File.dirname(__FILE__), imgname )
  213.     img = Wx::Image.new(filename, Wx::BITMAP_TYPE_PNG)
  214.     img.set_mask
  215.     if Wx::PLATFORM == "WXMSW"
  216.       img = img.scale(16, 16)
  217.     elsif Wx::PLATFORM == "WXGTK"
  218.       img = img.scale(22, 22)
  219.     end
  220.     # WXMAC can be any size up to 128x128, so don't scale
  221.     icon = Wx::Icon.new
  222.     icon.copy_from_bitmap(Wx::Bitmap.new(img))
  223.     return icon
  224.   end
  225.  
  226.   def create_popup_menu
  227.     # Called by the base class when it needs to popup the menu
  228.     #  (the default evt_right_down event).  Create and return
  229.     #  the menu to display.
  230.     menu = Wx::Menu.new
  231.     menu.append(TBMENU_OPTION,     "Option")
  232.     menu.append_check_item(TBMENU_STAY_ON_TOP,"Stay On Top")
  233.     menu.check(TBMENU_STAY_ON_TOP, @stay_on_top)
  234.     submenu = Wx::Menu.new
  235.     submenu.append(TBMENU_MOVE_RU, "Right Upper")
  236.     submenu.append(TBMENU_MOVE_RB, "Right Bottom")
  237.     submenu.append(TBMENU_MOVE_LU, "Left Upper")
  238.     submenu.append(TBMENU_MOVE_LB, "Left Bottom")
  239.     menu.append_menu(Wx::ID_ANY,   "Move",submenu)
  240.     menu.append(TBMENU_EXIT,       "Exit")
  241.     return menu
  242.   end
  243. end
  244.  
  245. class App < Wx::App
  246.   def on_init
  247.     f =  ::RubyThroughClock.new
  248.     f.show
  249.   end
  250. end
  251. App.new.main_loop
  252.  

実行結果はこんなかんじです。アイコンの画像はThe Ruby Visual Identity Teamのモノをとりあえず使わせてもらっています。新しいアイコンが出来たらいずれリプレースする予定です。

02

時刻表示の画面です。着実に時計らしくなってきています。

01

The Ruby Logo is Copyright (c) 2006, Yukihiro Matsumoto. It is licensed
under the terms of the Creative Commons Attribution-ShareAlike 2.5
agreement:

http://creativecommons.org/licenses/by-sa/2.5/

We ask that you do not use the logo to represent something other than
the Ruby Programming language. If you have questions about the logo,
please join the VIT-Discuss mailing list and ask your questions there:

http://rubyforge.org/mailman/listinfo/vit-discuss/

Thank you,

The Ruby Visual Identity Team

|

« 時計をつくる3 | トップページ | 時計をつくる5 »

時計をつくる」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/1201593/29648142

この記事へのトラックバック一覧です: 時計をつくる4:

« 時計をつくる3 | トップページ | 時計をつくる5 »