Ruby/Tk:キャンバス上のアイテム作成
Ruby/Tkでキャンバス上へのアイテム作成で、以下のように書く例がよく紹介されている:
#!ruby require "tk" c = TkCanvas.new {pack} TkcLine.new(c, 0, 0, 50, 50) Tk.mainloop
だが、これだとアイテム生成毎にTkcLine.new(…やら、TkcRectangle.new(…やらと常にTkcを付けて、またアイテム種をキャピタライズして書かなければならず面倒で、アイテムを生成するキャンバス情報もいちいち持ち回る必要がある。
他の言語の呼び出しはもっと簡単(気分的に?)になっていて、Tclでのサブコマンドの利用とほぼ同様なのでそれほど違和感がない:
- TkDocs – Tk Tutorial – Canvas
- Tcl: .canvas create line 10 10 200 50
- Ruby: TkcLine.new( canvas, 10, 10, 200, 50)
- Perl: $canvas->create_line(10,10,200,50);
- Python: canvas.create_line(10, 10, 200, 50)
そこで、これを他の言語(PerlやPython)と同様にできないのかと、以下のようなTkCanvasクラスへのアイテム生成のためのメソッド追加を考えてみた:
class TkCanvas def create_arc(*args); TkcArc.new(self, args); end def create_bitmap(*args); TkcBitmap.new(self, args); end def create_image(*args); TkcImage.new(self, args); end def create_line(*args); TkcLine.new(self, args); end def create_oval(*args); TkcOval.new(self, args); end def create_polygon(*args); TkcPolygon.new(self, args); end def create_rectangle(*args); TkcRectangle.new(self, args); end def create_text(*args); TkcText.new(self, args); end def create_window(*args); TkcWindow.new(self, args); end end
(当初は、メソッド名をcreate_lineなどでなくlineだけ、とか、create_polygonは短くpolyとすることも考えたのだが、他の言語の状況にも合わせてみた)
これで呼び出しをすると記述が簡単になって便利だが、改めてRubyのライブラリファイルのtk/canvas.rb【注1】
- 【注1】
- ファイルの場所はWindowsだと「C:/Ruby21-x64/lib/ruby/2.1.0/」など。また、このファイルは上位フォルダのtkcanvas.rbでrequireされている)
を見てみると、内部にcreateメソッドがあり、TkCanvasインスタンスへの以下のような指示で作成できることがわかった:
c = TkCanvas c.create("line", 10, 10, 200, 50)
アイテム個別のTkcXxxxクラスを使ってアイテムを生成するより、こちらのほうが把握しやすいのでは。
さらに見ていると、このTkCanvas::createメソッドでは、以下のようにTkcItemクラスのtype2classメソッドを使って、アイテム文字列からクラスへのマッピングを行なっていた:
class Tk::Canvas<TkWindow include TkCanvasItemConfig ... def create(type, *args) if type.kind_of?(Class) && type <TkcItem # do nothing elsif TkcItem.type2class(type.to_s) type = TkcItem.type2class(type.to_s) else fail ArgumentError, "type must a subclass of TkcItem class, or a string in CItemTypeToClass" end type.create(self, *args) end ... end
同ファイルの下部にあるTkcItemクラスを見ていくと、type2classメソッドではアイテム名からクラスへの対応表をCItemTypeToClassハッシュに保持している:
class TkcItem<TkObject ... def TkcItem.type2class(type) CItemTypeToClass[type] end ... end
個々のアイテムクラスの情報は、さらに下部にあるクラス定義で行われていた:
class TkcLine<TkcItem CItemTypeName = 'line'.freeze CItemTypeToClass[CItemTypeName] = self end ...
このCItemTypeToClassハッシュの情報を使うと、TkCanvasへのcreate_xxxxメソッドの登録は以下のようにひとまとめにできる(class_evalを使った):
class TkCanvas TkcItem::CItemTypeToClass.each {|k, v| class_eval("def create_#{k}(*args); #{v}.create(self, *args); end")} end
上記を使うと以下のように記述できる:
c = TkCanvas c.create_line(10, 10, 200, 50)
create_の入力は増えるわけだが、いちいちTkcXxxx.new(c, …)とせずこちらでいいんじゃないかなぁ。
リンク
- library tk
- class TkCanvas
||create
- class TkcItem
||type2class
- class TkcLine
- (RDocの不具合なのか親クラスの情報等が表示されない。また、Tkライブラリのクラス一覧でもTkcItemの下に配置されない)
- class TkCanvas
- Index of Classes & Methods in tk: Ruby Standard Library Documentation (Ruby 2_2_0)
- Class: Tk::Canvas (Ruby 2_2_0)
||method-i – create
- Class: TkcItem (Ruby 2_2_0)
||method-c – type2class
- Class: TkcLine (Ruby 2_2_0)
- Class: Tk::Canvas (Ruby 2_2_0)
- Module: Tk — Ruby 2.1.2 Standard Library
class_eval
- classキーワードとclass_evalメソッド – 紳士なブログ
- class_eval, module_eval (Module) – Rubyリファレンス
- 【Ruby】【メタプログラミング】メソッド名を動的に変更。 class_eval – せかいや
- Rubyのdefine_method、class_evalで動的に定義されたメソッドの呼出コストを調べてみた – Qiita
- やはりdefine_methodで定義したメソッドは遅い
- 文字列class_evalで定義したメソッドは通常のメソッドと呼出コストが変わらない
- やはりdefine_methodで定義したメソッドは遅い
Ruby/Tk紹介
- Rubyist Magazine – Ruby/Tk の動向
- 逆引きRuby/Tk
- Ruby/Tkによる簡単なGUI作成 | MARUのてくめも
- Ruby/Tk サンプルプログラム集
- Ruby/TK – Canvas Widget
- Ruby入門: Ruby/Tk:GUI: キャンバスのWidgetを作成 梶山 喜一郎
- Ruby/Tk入門