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年9月 | トップページ | 2011年12月 »

2011年11月の3件の記事

2011年11月21日 (月)

ようやく1本書き上げました(2) - BoxSizer

まず、とにかく Widget を並べて画面を作ります。

前回載せた画面のレイアウトですが、内部では、いくつかのパートを縦に積み上げるような形でレイアウトしています。

Img_0_01

赤枠でくくった部分ごとに作成して、Wx::BoxSizer( Wx::VERTICAL ) で積み上げていくことになります。

手始めに、一番上の赤枠 (url パート) だけの画面から始めてみます。この部分だけを抜き出すと

Img_1_00

こうなりますが、これがまた 3 つの widget を Wx::BoxSizer( Wx::HORIZONTAL ) で横に並べたものになります。

Img_1_01

ならべる widget は、左から順に Wx::StaticText, Wx::TextCtrl, Wx::Button になります。

それでは、まず、お約束でなにもない空っぽの window だけを表示するコードから


GUI_01.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 ) )
end
end

MyApp.new.main_loop

MyApp#on_init では、FrameMain を new して、show を呼んで表示させているだけです。FrameMain#initialize では super で Wx::Frame を呼び出して、親 window のないこと、window ID はシステムに適当にわりふってもらうこと、window タイトルにはスクリプトファイル名(この場合は、GUI_00.rb)を表示すること、を指定しています。それ以外にはなにもしていないので、実行すると、このような画面です。

Img_1_02

ここに widget を追加するコードを追加していきます。

# 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" )
  end
end

MyApp.new.main_loop

widget を 3 つ生成しました。いずれも new に与える引数は、一つ目が親 window, 二つ目が window ID (システムに任せる場合は -1 或いは Wx::ID_ANY), 以降は widget ごとに異なるので、WxRuby のリファレンス から、StaticText.new , TextCtrl.new , Button.new を参照します。

しかし、これをこのまま実行してみればわかりますが、表示が崩れています。そこで、sizer の登場です。まだ、BoxSizer しかつかっていませんが、これでかなりなんとかなるはずです。Java(Swing) なら BoxLayout, Tk なら pack ジオメトリマネージャに相当するものですね。


GUI_02.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::HORIZONTAL )
    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 )
    set_sizer( sizer )
  end
end

MyApp.new.main_loop

widget を生成してから、BoxSizer を生成しています。引数に Wx::HORIZONTAL を与えているので、この sizer に widget を追加すると、横に並んでいきます。sizer.add で生成済みの widget を text, ctrl, button の順に追加していき、最後に set_sizer で sizer を登録します。

set_sizer( sizer ) は、レシーバを省略して記述されているので、実際には暗黙のレシーバが使われて、self.set_sizer( sizer ) と同じことです。つまり、self = ここでは FrameMain の instance に対して、その表示レイアウトを sizer に委任するという内容になります。

ここでは、すべての widget を追加してから最後に set_sizer を call していますが、BoxSizer.new の次の行で、set_sizer しておいて、それから sizer に widget を追加していってもかまいません。

上記のコードを実行すると、このような画面になります。

Img_1_03

window の縦幅が必要以上に広いので不恰好ですが、一応、狙った形になりました。

BoxSizer#add の引数ですが、一つ目は見ればわかるように、追加する widget そのものですね。

2 つめの引数は、BoxSizer の担当方向 (今回の例では Wx::HORIZONTAL なので、横方向) の各 widget のサイズの「割合」を指定します。上記の GUI_01.rb では、text が 0, ctrl が1, button が 0 です。

最初から 0 / 1 / 0 ではわかりにくいので、これを 1 / 1 / 1 に変更して実行してみると、このようになります。

Img_1_04

それぞれの widget の横方向のサイズが 1 : 1 : 1 になりますので、つまり、1/3 ずつのサイズになります。また、1 / 1 / 2 に修正して実行すると次のようになります。

Img_1_05

横方向のサイズが 1 : 1 : 2 になるので、text と ctrl は 全体の 1/4, button は 2/4 = 1/2 のサイズになります。

GUI_01.rb のように、0 / 1 / 0 にすると、text と button の横サイズが 0 になるかというと、さすがにそれでは何も表示されなくなって具合が悪いので、表示するのに必要最小限のサイズ (これを個別に設定する method もありますが) で表示され、それ以上はサイズを占有しません。残りのスペースは、すべて 0 以外の値を指定した widget で分け合うことになるので、この例ではすべて ctrl が占めます。ためしに、window の横幅をマウスで延び縮みさせてみても、text と button はサイズを変えず、影響を受けるのは ctrl だけになります。

BoxSizer.add の第 3 引数は、'flag' という属性で、いくつかの情報を持ちます。今回の例では、まず、Wx::ALIGN_CENTER_VERTICAL を指定しているので、(担当範囲の) 画面の中で、上下方向の真ん中に位置します。window の縦幅を換えて、確認してみてください。

複数の (互いに衝突しない) 属性を指定するときには、'|' で論理和をとります。ここでは、Wx::ALL を指定していますが、これは、次に説明する border 幅を追加する方向です。Wx::ALL で上下左右の 4 方向に、たとえば Wx::LEFT を指定すれぱ左側だけに、border が追加されます。

第 4 引数は border 幅の指定です。border というのは、html でいうところの margin や padding のようなものです。百聞は一見に如かず、GUI_01.rb で、第 4 引数が '5' になっているところを、すべて '0' に修正して実行すると、以下のようになります。

Img_1_06

border は、なくても動作には影響しませんが、こうやって表示してみると、適度につけておいたほうが見栄えがよいですね。

とりあえず、今日はここまでとします。

2011年11月20日 (日)

ようやく1本書き上げました(1)

WxRuby に手をつけてから、しばらく行き詰まっていましたが、ようやく実働するものを1本書き上げました。

といっても、GUI, GUI しまくったものではなく、以前から CUI 用に作って便利につかっていたものを、ruby 1.8 → 1.9 対応に修正するついでに、GUI 対応にしただけです。だからたいしたものではないのですが、それでも得るところはいろいろありました。当面、CUI 版に GUI をかぶせるところから勘をつかんで行きたいと思います。

今回から何回かに分けて、ソースを晒していきますので、もし、「ここはもっとこうした方がよい」というアドバイスがあれば、是非よろしくお願いします。

まず、もともとの CUI レベルのプログラムがどんなものか、簡単に説明します。

仕事関係でいくつか専門雑誌を買っていますが、読みきる前に次の号が届いて、どんどんたまっていきます。古いのは捨てていけばよいのでしょうが、いつ必要になるかわからないので、なかなかそうもいきません。こういう場合の定番で、もう 5 年以上前から自炊して PDF で保存しています。これでスペースの問題は解決ですが、どの号にどの記事が載っていたかインデックスを作っておかないと、価値も半減です。

最初は目次を全部手入力していましたが、直ぐ嫌になりました。その次は OCR にかけましたが、結構手直しが必要で、レイアウトも崩れるので意外と省力化できません。これも直ぐ嫌になって、結局各出版社の web site から、バックナンバーの目次ページをいただいてきて、必要な情報だけを抜き出すスクリプトを書きました。

このスクリプト自体は、結構行数があるので、すべて掲載はできませんが、実際に動くコードがないと、この後の説明が面倒になりますので、最低限のキモの部分だけにしたコードを載せます。


sub_a.rb

# encoding: shift_jis

require "net/http"
require "nkf"

Net::HTTP.version_1_2

class Journal
  def initialize
    @abstract = ""
    @log = ""
    @default_out_file_name = ""
  end
  attr_reader :abstract, :log, :default_out_file_name

  def scan_web_page( url )
    @log = get_html_body( url )

    @abstract = ""
    source = @log
    while( /<h\d.*?\/h\d\s*>/i =~ source )
      @abstract << $& << "\n"
      source = $'
    end

    @default_out_file_name = if ( /<title.*?>([^<]*)<\/title/i =~ @log )
      NKF.nkf( '-s', $1[0,8] ) + ".txt"
    else
      "abstract.txt"
    end
  end

private
  def get_html_body( url )
    %r{^http://([^/]+)/} =~ url
    server, path = [ $1, "/" + $' ]
    http = Net::HTTP.new( server )
    http.start
    response = http.get( path )
    case response
    when Net::HTTPRedirection
      response = http.get( response[ "Location" ] )
    end
    http.finish
    response.body
  end
end

class SUB_A < Journal
  def name; "Journal A"; end
end

if $0 == __FILE__
  j = SUB_A.new
  j.scan_web_page( ARGV[0] )

  unless ( $DEBUG )
    print j.abstract
  else
    print j.log
  end
end

ブラウザから url をコピーしてきて、コマンドプロンプトから以下のように実行すると、<h?>...</h?> で mark up されている見出しの部分だけを抽出して出力します。

> ruby sub_a.rb "url"

また、デバッグスイッチをつけて実行すると、抽出する前の html をそのまま出力します。 (但し、文字コードの変換は考慮していないので、shift_jis のページ以外は文字化けします)

> ruby -d sub_a.rb "url"

このスクリプトに実装する機能は、url を与えると、html を get してきて、必要な文字列だけ抜き出す(SUB_A#scan_web_page)、抽出した文字列を返す(SUB_A#abstract)、html の内容に応じて file に save するときの default file 名 (たとえば 2011-11.txt とか) を生成する(SUB_A#scan_web_page の一部)、その file 名を返す(SUB_A#default_out_file_name)、抽出がうまくいかなかったときに、解析の途中経過を確認できるデバッグ情報を返す(SUB_A#log)、雑誌の名前を得る(SUB_A#name)、といったところです。

実際に使っているスクリプトもほぼ上記と同様のインターフェイスですが、もちろん目次の抽出や解析情報については、もっと複雑な処理をしています。

今回、上記のスクリプトに、このような GUI をかぶせるコードを書きました。

Img_0_00_2

次回から、この GUI の作成過程を説明していきます。

2011年11月 3日 (木)

参考書買って来ましたが・・・

web site のリファレンスで wxRuby の各クラスの使い方はなんとなくわからんではないのですが、文法とライブラリがわかるというのと、プログラムが書けるというのとは別問題なので、何か定番なアプリケーションをひとつ作ってみるみたいな参考書がないものだろうかと探してみました。

ですが、まあ、さすがに wxRuby の本というのはありません(^^;

ならば、この際、本家の wxWidgets の本でいいやと探してみたのですが、これも見つからない。仕方がないので、洋書で Cross-Platform GUI Programming with wxWidgets を買ってみました

5157x4kqhkl_sl500_aa240_

さすがに英語だと、日本語と同じスピードでは読めませんが、所詮は技術書なので、大体でも読むことができます。クラスの使い分けなんかも書いてるので、買わないよりは参考になりました。

ちなみにこれ、結構安かったですね。3600円くらい。

日本語でプログラミング関係の本を買うときに目安にしているのが、5000円以上するかどうかなのですが、過去、大体 5000 円くらいはしないと内容が中途半端で役に立たなかった印象があります。今回は洋書だから輸入分高くなるかと思ってたのに、むしろ安い。まあ、日本のほど上質紙使ってないから、そういうところが利いているのかもしれません。

しかし、まだ全部読んだわけではないのですが、残念ながら、リファレンスから大きく踏み出した内容にまではなっていないようで・・・。

web の記事で Rails に手を出すとき、scaffold までは簡単に書いてあるけれど、実際の勉強は scaffold 以上のものを作るところにある、っていうのと同じような位置づけの参考書が欲しかったのですが、そこまでの内容ではありませんでした。

次の手としては、やっぱり付属のサンプルを端から読み下してみましょうか。何か1本、へたくそでもいいから書いてしまえば、そこから勘がつかめるものなのですが、どうにもまだ wxRuby の勘所がつかめません。

« 2011年9月 | トップページ | 2011年12月 »