Rubyでスクリーンショットサーバ(2)

Mozshotさんがベースにしたという、このソースを読んでみることにします。
*1

まずは動かしてみます。ソースをダウンロード後、以下のスクリプトを用意します。


snap.rb


#!/usr/bin/ruby

ENV['DISPLAY'] = ':1.0'
require 'gtkmozembed'
require 'moz-snapshooter'

Gtk.init
MozSnapshooter.new
Gtk.main

以下のように実行してみます。



[root@devel ~]# Xvfb :1 -screen 0 1024x768x24 &
[1] 19816

_XSERVTransSocketOpenCOTSServer: Unable to open socket for inet6
_XSERVTransOpen: transport open failed for inet6/xxxxxxxxxx:1
_XSERVTransMakeAllCOTSServerListeners: failed to open listener for inet6
Could not init font path element /usr/X11R6/lib/X11/fonts/CID/, removing from list!

[root@devel ~]# ruby snap.rb
1
Wrote screenshot-thumb.png

Could not init font path element /usr/X11R6/lib/X11/fonts/CID/, removing from list!

なにやらエラーが出ていますが、とりあえあずは無視します。
カレントディレクトリにscreenshot-thumb.pngというファイルが作成されていれば成功です。

勉強の意味で、勝手にソースにコメントをつけてみました(snapshotメソッドのところはまだ理解してませんが。)。

moz-snapshooter.rb コメント付き


#
# MozSnapshooter
# Web site thumbnailer
#
# Copyright (C) 2005 Mirko Maischberger
# Released in the Public Domain
#
# From an idea by Andrew McCall -
# http://www.hackdiary.com/archives/000055.html

require 'gtkmozembed'


#########################################################
#
# MozSnapshooterクラス
# Gtk::Windowのサブクラス
#
#########################################################

class MozSnapshooter < Gtk::Window

#########################################
#
# initializeメソッド
#
#########################################

def initialize

# Gtk::Window.initialize(=Gtk::Window.new)を呼び出す
# 他のウィジェットを含むことのできる新しいコンテナ(トップレベルウィンドウ)を作成
super

# selfはGtk::Window.newでできたオブジェクト
# コンテナのタイトルとボーダーを設定
# Gtk::Window#title=
# Gtk::Container#border_width=

self.title="MozSnapshooter"
self.border_width = 1

# width,highを設定
self.resize(780, 570)

# プロファイルのパスを設定
# ENV['HOME'].mozillaディレクトリにRubyGeckoプロファイルを作成
# ENV['HOME']の最後にスラッシュがないので、~/配下ではなく
# username.mozillaというディレクトリがつくられてしまった…

Gtk::MozEmbed.set_profile_path(ENV['HOME'] + '.mozilla', 'RubyGecko')

# Gtk::Container#<<
# Gtk::Container#addと同じ。
# コンテナ(トップレベルウィンドウ)にMozEmbedウィジェットを追加 している

self << Gtk::MozEmbed.new

# childって何?おそらくコンテナに追加されたMozEmbedウィジェットのオブジェクトだろう
# chrome(クロム)のフラグをセットしている
# chromeはmozillaの外観などに対する設定ファイルがあるディレクトリ。
# ALLCHROMEなので、すべてのchromeをセットしている。
# http://cow.neondragon.net/index.php/415-Fun-With-Firefox-Chrome-Urls

self.child.chrome_mask = Gtk::MozEmbed::ALLCHROME

# Gtk::Widget#set_size_request
# MozEmbedウィジェットのサイズを指定
# なぜかコンテナより大きい

self.child.set_size_request(816,600)

# GLib::Instantiatable#signal_connect
# net_stopシグナルを受信したら、on_net_stopハンドラを呼び出す。
# net_stopシグナルはページをロードし終わったら、発生する。
# asincronouslyって何?

self.child.signal_connect("net_stop") { on_net_stop }

# http://www.ruby-lang.orgをロード

self.child.location = "http://www.ruby-lang.org"

# インスタンス変数
# net_stopシグナルを受信してスクリーンショットを取るまでの
# 時間を定義している(1/1000秒単位)

@countdown = 2

# destroyシグナルを受信したら、終了。
# The user is bored, let's quit.

self.signal_connect("destroy") do
$stderr.print "closing...\n"
Gtk.main_quit
end

self.show_all
end

#########################################
#
# on_net_stopメソッド
#
# 先にあったnet_stopシグナルのハンドラを定義
# 1/1000秒後、falseがリターンするまでブロックを実行。
# @countdownで定めた時間が経過したら、
# screenshotメソッドを実行。
#
#########################################

def on_net_stop
Gtk::timeout_add(1000) do
@countdown -= 1
if(@countdown > 0)
puts @countdown
true
else
screenshot
false
end
end
end

#########################################
#
# screenshotメソッド
#
# スクリーンショットを作成
#
#########################################

def screenshot

# Gtk::MozEmbed#parent_window
# 親のウィンドウ(コンテナ)を取得
gdkw = self.child.parent_window

# geometryを取得
x, y, width, height, depth = gdkw.geometry
width -= 16
pixbuf = Gdk::Pixbuf.from_drawable(nil, gdkw, 0, 0, width, height)
pixbuf = pixbuf.scale(320, 200, Gdk::Pixbuf::INTERP_HYPER)
pixbuf.save("screenshot-thumb.png","png")
puts "Wrote screenshot-thumb.png"
Gtk.main_quit
end

end

ブラウジング・スナップショットとも外部コマンドに頼ってませんし、ブラウジング終了のシグナルを受信してからスナップショットをとっているところがいいです。なんかPythonのソースと似てますね。Rubyの勉強が終わったらPythonにも手をつけてみようと思います。

次はいよいよMozshotさんのソースに挑戦してみようと思います。

*1:ちなみに私のプログラミングスキルは、
Perl->ハッシュのリファレンスがわかるくらい
C->しばらく使ってないとポインタを忘れる
Ruby->いきなりrailsに手を出して四苦八苦
という感じです。オブジェクト指向もよくわかっていません。
ですので、その程度のレベルということでお願いします。