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    
無料ブログはココログ

« 2012年3月 | トップページ | 2012年6月 »

2012年5月の1件の記事

2012年5月28日 (月)

画像を貼り付ける(3) - ListCtrl でサムネイル

 前回までで、個別の画像を表示する方法はなんとなくわかりましたが、今度は当然のように、複数の画像を一覧して選びたくなりました。(いわゆるサムネイル一覧)

 これが、意外とてこずってしまい、そのため参考にしたサイトがどこだったか、わからなくなってしまいました。 Wx::ListCtrl を使ってサムネイル一覧を表示、画像そのものは Wx::ImageList  を経由して扱う、というやり方はどこかのサイトからカンニングしたので、是非リンクを張りたかったのですが。

 とりあえずコードです。実行したら、画面上部の [Load...] ボタンをクリック、複数の画像ファイルが存在するフォルダを選択すると、画像とファイル名の一覧が表示されます。Window の拡大縮小でレイアウトがついてこないなど、まだ不満はありますが、まずここからです。

# encoding: UTF-8

require 'wx'

class MyApp < Wx::App
  def on_init
    @main_frame = ThumbnailsFrame.new
    @main_frame.show
  end
end

class ThumbnailsFrame < Wx::Frame
  THUMB_W = 120
  THUMB_H =  90

  def initialize
    super(nil, :title => "Thumbnails", :size => [640, 480])
    set_sizer(Wx::BoxSizer.new( Wx::VERTICAL ))

    load_button = Wx::Button.new(self, :label => "Load...")
    get_sizer.add_item(load_button)
    evt_button( load_button ) { |evt| on_load }

    @thumb_list = Wx::ListCtrl.new(self, :style => Wx::LC_ICON)
    get_sizer.add_item(@thumb_list, :proportion => 1, :flag => Wx::EXPAND)

    create_status_bar
  end

  private
  def on_load
    dlg = Wx::DirDialog.new(self)
    if dlg.show_modal == Wx::ID_OK
      load_thumbnails(dlg.get_path)
    end
  end

  def load_thumbnails(dir)
    @thumb_list.clear_all
    image_list = Wx::ImageList.new(THUMB_W, THUMB_H)
    @thumb_list.set_image_list(image_list, Wx::IMAGE_LIST_NORMAL)

    item = Wx::ListItem.new

    ptn = "#{dir}/*.jpg\0#{dir}/*.png".gsub( /\\/, '/' )

    Dir.glob( ptn ).sort.each do |img_file|
      set_status_text("サムネイルを作成中...#{img_file}")

      idx = image_list.add(thumbnail_bitmap(img_file))
      unless ( idx < 0 )
        item.id = idx
        item.image = idx
        item.text = img_file
        @thumb_list.insert_item( item )
      else
        print "add image error : #{img_file}\n"
      end
      item.clear
    end
    set_status_text("サムネイルの作成が完了しました。")
  end

  def thumbnail_bitmap(file)
    img = Wx::Image.new(file)
    img_w, img_h = img.get_width, img.get_height
    ratio_w, ratio_h = THUMB_W.to_f/img_w.to_f, THUMB_H.to_f/img_h.to_f

    if ( ratio_w < ratio_h )
      new_h = (img_h * ratio_w).to_i
      img.rescale(THUMB_W, new_h)
      img.resize([THUMB_W,THUMB_H],Wx::Point.new( 0,(THUMB_H - new_h)/2 ))
    else
      new_w = (img_w * ratio_h).to_i
      img.rescale(new_w, THUMB_H)
      img.resize([THUMB_W,THUMB_H],Wx::Point.new( (THUMB_W - new_w)/2,0 ))
    end

    Wx::Bitmap.from_image(img)
  end
end

MyApp.new.main_loop

Wx::ListCtrl には、:style => Wx::LC_ICON 以外に LC_LIST とか LC_REPORT という形式もあるようですが、まず LC_ICON で使い方を覚えるのが基本のようです。で、直接サムネイル画像を list に追加していくのであればわかり易いのですが、 Wx::ImageList を経由する方法しか用意されていません。Wx::ListCtrl 以外にも Wx::TreeCtrl でも同様に使うらしいので、内部的にはメリットがあるのでしょう。

 ともかく、Wx::ImageList#add で、image_list に画像を追加登録すると、返り値として「image list の何番目か」という整数値が得られます。Wx::ListCtrl や、Wx::TreeList に使用する画像を指定するには、ListCtrl に、画像を溜め込んだ ImageList を割付けた上で、( ListCtrl#set_image_list( image_list, Wx::IMAGE_LIST_NORMAL) ) 「何番目か」という index 値を用いて指定します。画像を指定する Wx::ListCtrl#insert_item() にはいくつかの形式があって、直接 index や、文字列ラベルを与えて登録することもできますが、ここでは、Wx::ListItem の instance を使用して登録しています。上記のコードのように、ListItem の instance を作成しておいて、必要な属性を設定したうえで、この item を ListCtrl#insert_item() に与えるのが一番応用が効きます。ImageList 内の画像を指定する index 値を ListItem に設定して、ListCtrl に指定するという回りくどい構造になっています。

 ここまでだけであれば、WxRuby のドキュメント を順にたどっていけば、英語であることで幾分時間はかかるものの、もっと簡単だったのですが、ドキュメントを読んでいるだけではわからないトラップがあったので、ずいぶんと時間がかかってしまいました。

 「トラップ」というのは、ImageList に追加する画像の縦横サイズは、Wx::ImageLIst.new() で最初に指定した画像の縦横サイズに合わせておかないと、扱えないということです。

 正確には、完全に一致していなくても大丈夫な場合もあるようなのですが、一致させておくのが無難なようです。画像サイズを操作するのは、Wx::Image の方が得意なので、Image の method を用い、最後に Wx::Bitmap.from_image() で Wx::Bitmap に変換してから ImageList#add() を使います。このとき、登録する Bitmap 画像の縦横サイズが不適切だと、追加に失敗し、-1 が返ります。これもドキュメントにはありませんでした。

 ThumbnailsFrame#thumbnail_bitmap() の中で、rescale や resize を繰り返しているのは、元画像の縦横比を保った上でサムネイルを拡大・縮小し、ImageList に登録するための縦横サイズに合わせて、余白を追加する作業を行っています。試しに、"img.resize(...)" の行だけを削除して、ThumbnailsFrame#load_thumbnails() 内の "unless ( idx < 0 ) ..." を外して実行してみて下さい。表示されるサムネイル数がぐっと減ったり、画像が重複して表示されたり、と、なんともわかりにくい結果になります。

 次は、画面サイズに合わせてレイアウトを変えたり、サムネイルをクリックするイベントを拾い上げて、別 window で、その画像を表示したりといった機能を実装してみたいものです。

« 2012年3月 | トップページ | 2012年6月 »