2015年4月
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    
無料ブログはココログ

« 2011年11月 | トップページ | 2012年1月 »

2011年12月の2件の記事

2011年12月25日 (日)

ようやく1本書き上げました(4) - wxFormBuilder を使う - 前置き

前回までのコードは、実は最初から手書きで起こしたものではありません。

wxRuby のコーディングを思考錯誤している中で、ある程度までは手書きでやっていたのですが、やはりレイアウトだけはツールを使うほうが楽です。

wxRubyでGUIプログラミング - XRCを使う 」を参照して、wxFormBuilder を install し、XRC リソースを利用したコードに書き換えてみます。

その前に、前回のコードを一部直します。前回の rbox_part の部分ですが、

  def rbox_part
    Wx::RadioBox.new( self, -1, 'Select Journal',
      :choices => [ "Journal A", "Journal B" ],
      :style => Wx::RA_SPECIFY_COLS, :major_dimension => 2
    )
  end

上のコードですと、RadioBox に表示される選択肢が "Journal A" と "Journal B" の二つだけで固定になります。先々選択肢を増やしたくなったときに、動的に追加できればよいのですが、Wx::RadioBox のドキュメント を見ると、一旦生成した instance に、後から選択肢を追加することはできません。つまり、instance 生成時に与える選択肢を動的に与えられるコードにしておかなければなりません。

そこで、「ようやく1本書き上げました(1) 」に載せておいた sub_a.rb を使います。GUI_xx.rb とおなじdirectoryに sub_a.rb, sub_b.rb, sub_c.rb とファイルをコピーして rename します。各ファイルを編集して、たとえば sub_b.rb では、"SUB_A" を "SUB_B" に、"Journal A" を "Journal B" に置換します。

こうしておいて、前回 の GUI_03c.rb の先頭に以下のコードを追加し、rbox_part を書き換えます。


先頭に追加 (# encoding の行より後に追加する)


SUBs = Dir.glob( File.dirname( __FILE__ ) + '/sub*.rb' )
SUBs.each{ |s| require_relative s }
Journals = SUBs.map{ |s| File.basename( s, '.*' ).upcase }.map{ |j| self.class.const_get(j).new }

rbox_part を差し替え


    Wx::RadioBox.new( self, -1, 'Select Journal',
      :choices => Journals.map{ |j| j.name },
      :style => Wx::RA_SPECIFY_COLS, :major_dimension => 2
    )

これで、sub_a.rb, sub_b.rb, sub_c.rb を用意してから実行すると、次のような画面になるわけです。

Img_4_01

これで、後から追加したい選択肢が増えても、ソースを変更せずに対応できるようになりました。

続いて、url_part の部分だけを wxFormBuilder を使って表示するコードを書いてみます。まず、 wxFormBuiler を起動します。以下の画面は version 3.1 のものです。起動すると、空っぽの project がひとつありますので、まず名前を変更します。

Img_4_02

変更するには、画面右側のプロパティシート(?)に名前を入力します。

Img_4_03_4

 

上図の 'MyProject1' の部分を選択して文字を入力すると名前が変わります。今回はこのままにしておきます。'relative_path' にチェックをいれます。'code_generation' が折りたたまれていたら '+' マークをクリックして上記のように展開し、C++ のチェックを外し、XRC にチェックをいれます。また、'file' には、後でセーブするときのファイル名を設定します。ここでは、プロジェクト名と同じにしておきます。

ここで手始めに作ってみるのは、url_part だけが表示できればよいので、Wx::Frame のレイアウト情報を作成します。MyProject1 をクリック・選択し、画面真ん中上部のタブから 'Forms' を選びます。左端のボタンが Frame のものなので、これをクリックします。

Img_4_04

更に、タブから 'Layout' のタブを選び、左端のボタンをクリックすると、BoxSizer が追加されます。

Img_4_05

Img_4_06

今度は、プロパティを変えてみます。BoxSizer の名前を 'sizer_url' に、また、横方向の BoxSizer が欲しいので、'orient' の値は、wxHORIZONTAL にします。(wxRuby 用のツールではなく、wxWidgets 用のツールなので、Wx::HORIZONTAL ではなく wxHORIZONTAL となっていますが、まぁ分かりますよね)

Img_4_07

同様に、中央のタブで 'Common' を選び、左のペインで BoxSizer を選択、中央のボタンから StaticText, TextCtrl, Button を順にクリックすると、下のようになります。

Img_4_08

それぞれのプロパティを適切に設定しましょう。

まず、StaticText を選択して、右のプロパティシートをいじります。

Img_4_09

'name' と、'label' を変更しました。'name' はいわゆる識別子ですね、最終的に変数名のように使うことになります。'label' は画面上に表示される文字列です。更に、プロパティシートを下の方にスクロールします。

Img_4_10

wxALL は最初からチェックされていました。既に説明したように、wxALL は、widget の四方に余白をつけるという指定で、余白幅の border もこの画面で既に '5' と設定されています。(というか、この設定を手書きに書き起こしたのが、これまでのソースだったわけですが) ここでは、wxALILGN_CENTER_VERTICAL にチェックをいれました。これで、画面の上下中央に StaticText が位置することになります。

続いて TextCtrl のプロパティを設定します。

Img_4_11

'name' を ctrl_url に変更しただけです。'value' に文字列を入力しておくと、初期値として最初から表示されますが、今回は空白の TextCtrl にします。StaticText と同様に、wxALIGN_CENTER_VERTICAL もチェックするのですが、TextCtrl には、もうひとつ設定したい属性があります。

Img_4_12

'proportion' に 1 を設定します。これも説明済みですね。

最後に Button を設定します。画面は省略しますが、'name' を 'button_url' に、'label' を 'browse' に、wxALIGN_CENTER_VERTICAL をチェックします。

このとき、中央のペインには、このような Frame が表示されていると思います。

Img_4_13

ここまで来たら、ファイルに保存します。[File] メニューから [Save] を選んで、適当なファイル名でセーブします。これは、これまで作成してきた *.rb ファイルと同じ directory にセーブしてください。仮に 'project_file.fbp' としておきましょう。ここでセーブされたファイルは、ここまでの全作業内容が保存されているので、PC を再起動しても、ここから再開できます。

次に [File] メニューから [Generate Code] を選択します。そうすると、上記の project_file.fbp と同じ directory に MyProject1.XRC というファイルが保存されます。ファイル名の MyProject1 は、Project のプロパティシートをいじるときに設定したものです。この MyProject1.XRC はエディタでのぞいてみれば分かることですが、XML ファイルです。

まだまだ先は長いのですが、とりあえずファイル保存までたどり着いたので、今日はここまでとします。

2011年12月24日 (土)

ようやく1本書き上げました(3) - BoxSizerの組み合わせ

Img_0_01

前回に続き、上の画面レイアウトの、上から 2 枠目を追加していきます。

枠の中身だけであれば、Wx::RadioBox だけでできているので、Wx::RadioBox.new を使って、これだけで作れます。

    Wx::RadioBox.new( self, -1, 'Select Journal',
      :choices => [ "Journal A", "Journal B" ],
      :style => Wx::RA_SPECIFY_COLS, :major_dimension => 2
    )

:choices => で選択肢を配列の形で指定、:style => で行数を指定するか列数を指定するかを決定、:major_dimension => で指定する行数を決定します。上記の例では、「列」で、「2」なので、選択肢が 2 列に配置されます。

これを、前回の url パートの下に追加して並べます。ただ単にだらだらと書き下すだけなら、これでも動きます。


GUI_03a.rb


# encoding: utf-8

require "wx"

class MyApp < Wx::App
  def on_init
    @frame_main = FrameMain.new
    @frame_main.show
  end
end

class FrameMain < Wx::Frame
  def initialize
    super( nil, -1, $0 )
    set_background_colour( Wx::Colour.new( 240, 240, 255 ) )
    text = Wx::StaticText.new( self, -1, "    url :" )
    ctrl = Wx::TextCtrl.new( self, -1 )
    button = Wx::Button.new( self, -1, "Paste" )

    sizer = Wx::BoxSizer.new( Wx::VERTICAL )
    set_sizer( sizer )

    sizer_url = Wx::BoxSizer.new( Wx::HORIZONTAL )
    sizer_url.add( text,0, Wx::ALIGN_CENTER_VERTICAL | Wx::ALL , 5 )
    sizer_url.add( ctrl, 1, Wx::ALIGN_CENTER_VERTICAL | Wx::ALL , 5 )
    sizer_url.add( button, 0, Wx::ALIGN_CENTER_VERTICAL | Wx::ALL , 5 )
    sizer.add( sizer_url, 0, Wx::EXPAND )

    radio = Wx::RadioBox.new( self, -1, 'Select Journal',
      :choices => [ "Journal A", "Journal B" ],
      :style => Wx::RA_SPECIFY_COLS, :major_dimension => 2
    )
    sizer.add( radio, 0, Wx::LEFT | Wx::RIGHT | Wx::EXPAND, 5 )
  end
end

MyApp.new.main_loop

url の部分をレイアウトするのに、横方向の BoxSizer を使用しましたが、更にこれらを縦に積みあけるとなると、縦方向の BoxSizer が必要になります。そのため、set_sizer() で指定する、メインの Sizer を VERTICAL として、url 用の Sizer を別に sizer_url として作成し、sizer に sizer_url を add すれば、縦に積んでいけるというわけです。

つまり、BoxSizer#add の一つ目の引数には、widget だけでなく、sizer も指定できるというわけです。

BoxSizer#add の第三引数に Wx::EXPAND が指定してありますが、これは、「その BoxSizer が担当する方向と直行する方向に目いっぱい広げる」という指定です。

ここでは、top level の sizer は縦方向に部品を積み上げる担当なので、Wx::EXPAND を指定すると、直行する方向、つまり「横方向」に広げます。これによって、url パート、および RadioBox が、window の横幅いっぱいに広がります。ためしに Wx::EXPAND なしで実行すると、このようになります。

Img_2_02

さて、この調子で次々と BoxSizer に追加していけばレイアウトを組めるのですが、流石に見づらくなってくるので、ちょっと整理します。


GUI_03b.rb


# encoding: utf-8

require "wx"

class MyApp < Wx::App
  def on_init
    @frame_main = FrameMain.new
    @frame_main.show
  end
end

class FrameMain < Wx::Frame
  def initialize
    super( nil, -1, $0 )
    set_background_colour( Wx::Colour.new( 240, 240, 255 ) )

    sizer_top = Wx::BoxSizer.new( Wx::VERTICAL )
    set_sizer( sizer_top )

    sizer_top.add( url_part, 0, Wx::EXPAND )
    sizer_top.add( rbox_part, 0, Wx::LEFT | Wx::RIGHT | Wx::EXPAND, 5 )
  end

  def url_part
    sizer = Wx::BoxSizer.new( Wx::HORIZONTAL )
    text = Wx::StaticText.new( self, -1, "    url :" )
    ctrl = Wx::TextCtrl.new( self, -1 )
    button = Wx::Button.new( self, -1, "Paste" )
    sizer.add( text,0, Wx::ALIGN_CENTER_VERTICAL | Wx::ALL , 5 )
    sizer.add( ctrl, 1, Wx::ALIGN_CENTER_VERTICAL | Wx::ALL , 5 )
    sizer.add( button, 0, Wx::ALIGN_CENTER_VERTICAL | Wx::ALL , 5 )
    sizer
  end

  def rbox_part
    Wx::RadioBox.new( self, -1, 'Select Journal',
      :choices => [ "Journal A", "Journal B" ],
      :style => Wx::RA_SPECIFY_COLS, :major_dimension => 2
    )
  end
end

MyApp.new.main_loop

まとまったそれぞれの部品群を全て initialize の中で定義するのではなく、サブルーチンに下請けに出して、sizer_top で縦積みする実装です。この形で、更にもうひとつパートを追加して今日の締めとします。


GUI_03c.rb


# encoding: utf-8

require "wx"

class MyApp < Wx::App
  def on_init
    @frame_main = FrameMain.new
    @frame_main.show
  end
end

class FrameMain < Wx::Frame
  def initialize
    super( nil, -1, $0 )
    set_background_colour( Wx::Colour.new( 240, 240, 255 ) )

    sizer_top = Wx::BoxSizer.new( Wx::VERTICAL )
    set_sizer( sizer_top )

    sizer_top.add( url_part, 0, Wx::EXPAND )
    sizer_top.add( rbox_part, 0, Wx::LEFT | Wx::RIGHT | Wx::EXPAND, 5 )
    sizer_top.add( out_or_scan_part, 0, Wx::EXPAND )
  end

  def url_part
    sizer = Wx::BoxSizer.new( Wx::HORIZONTAL )
    text = Wx::StaticText.new( self, -1, "    url :" )
    ctrl = Wx::TextCtrl.new( self, -1 )
    button = Wx::Button.new( self, -1, "Paste" )
    sizer.add( text,0, Wx::ALIGN_CENTER_VERTICAL | Wx::ALL , 5 )
    sizer.add( ctrl, 1, Wx::ALIGN_CENTER_VERTICAL | Wx::ALL , 5 )
    sizer.add( button, 0, Wx::ALIGN_CENTER_VERTICAL | Wx::ALL , 5 )
    sizer
  end

  def rbox_part
    Wx::RadioBox.new( self, -1, 'Select Journal',
      :choices => [ "Journal A", "Journal B" ],
      :style => Wx::RA_SPECIFY_COLS, :major_dimension => 2
    )
  end

  def out_or_scan_part
    sizer0 = Wx::BoxSizer.new( Wx::VERTICAL )

    sizer1 = Wx::BoxSizer.new( Wx::HORIZONTAL )
    sizer1.add( Wx::StaticText.new( self, -1, "出力 dir :" ),
      0, Wx::ALL | Wx::ALIGN_CENTER_VERTICAL, 5 )
    sizer1.add( Wx::TextCtrl.new( self, -1 ), 1,
      Wx::ALL | Wx::ALIGN_CENTER_VERTICAL, 5 )
    sizer1.add( Wx::Button.new( self, -1, "Browse" ), 0,
      Wx::ALL | Wx::ALIGN_CENTER_VERTICAL, 5 )
    sizer0.add( sizer1, 0, Wx::EXPAND )

    sizer2 = Wx::BoxSizer.new( Wx::HORIZONTAL )
    sizer2.add( Wx::Window.new( self, -1 ), 1 )
    sizer2.add( Wx::Button.new( self, -1, "Scan → Default file で出力" ),
      0, Wx::EXPAND | Wx::ALL, 5 )
    sizer2.add( Wx::Window.new( self, -1 ), 1 )
    sizer2.add( Wx::Button.new( self, -1, "出力せず Scan のみ" ),
      0, Wx::EXPAND | Wx::ALL, 5 )
    sizer2.add( Wx::Window.new( self, -1 ), 1 )
    sizer0.add( sizer2, 0, Wx::EXPAND )

    sizer0
  end
end

MyApp.new.main_loop

実行結果です。なお、sizer2 に widget を並べる過程で、Wx::Window を空白の詰め物として使っています。

Img_2_03

« 2011年11月 | トップページ | 2012年1月 »