Ruby GTK2

目次

シグナルハンドラ

GTK+では,ウィジェットに対するユーザの入力(イベント)と処理(シグナルハンドラ)を結びつける仕組みとしてシグナルがある.

シグナルを受信したシグナルハンドラは,シグナルを発行させたイベントの情報を取得できる.

後述のサンプルコードは,「はじめに」でシグナルハンドラを説明した際に紹介したものである.

#!/usr/bin/ruby

require 'gtk2'

win = Gtk::Window.new()
win.signal_connect("destroy") do
  Gtk.main_quit()
end
win.show()
Gtk.main()
"destroy"シグナルは,ウィンドウの「閉じる」ボタンを押したり終了キー(WindowsでいうAlt+F4)を入力することで発行される.

イベント

このプログラムに新たなシグナルハンドラを追加したサンプルコードを紹介する.以下のプログラムは,ウィンドウ自体に独自の終了キーを追加したサンプルコードである.

#!/usr/bin/ruby

require 'gtk2'

win = Gtk::Window.new()
win.signal_connect("destroy") do
end

win.signal_connect("key_press_event") do |wdt, evt| # ウィンドウ上でキーを押された際
  if evt.state==Gdk::Window::CONTROL_MASK then # Controlキーを押したまま
    case evt.keyval
    when Gdk::Keyval::GDK_w:
      puts "quit"
      Gtk.main_quit()
    end
end

win.show()
Gtk.main()
このプログラムはControlキー+wを押すことで標準出力に"quit"を出力して終了する.

シグナルハンドラを定義するためのブロックは,仮引数を指定することでイベントを発行したウィジェットやそのイベント情報を取得できる.前述のサンプルでは,"key_press_event"というウィンドウ上でキーを押された際に発生するイベントの情報をevtという仮引数で取得している."key_press_event"のイベント情報はGdk::EventKeyのインスタンスである.Gdk::EventKeyはkeyvalで入力されたキーの値,stateで修飾キー情報(GdkModifierType)を取得できる.

修飾キーを複数同時に押された場合,定数を論理和で繋ぐことで検知できる.以下に例を示す.

#!/usr/bin/ruby

require 'gtk2'

win = Gtk::Window.new()
win.signal_connect("destroy") do
end

win.signal_connect("key_press_event") do |wdt, evt| # ウィンドウ上でキーを押された際
  if evt.state==Gdk::Window::CONTROL_MASK then # Controlキーを押したまま
    case evt.keyval
    when Gdk::Keyval::GDK_w:
      puts "quit"
      Gtk.main_quit()
    end
  elsif  evt.state==Gdk::Window::CONTROL_MASK|Gdk::Window::SHIFT_MASK then # ControlキーとShiftキーを押したまま
    case evt.keyval
    when Gdk::Keyval::GDK_W
      puts "QUIT"
      Gtk.main_quit()
    end
  end
end

win.show()
Gtk.main()
このプログラムは,Controlキー+Shiftキー+wを押すことで,標準出力に"QUIT"を出力してプログラムを終了する.

なお扱うイベントの種類は,ウィジェットによって異なる.

Gdk::EventKey

取得可能な修飾キーを表す定数には次のようなものがある.

接続

GTK+では,各シグナルに対して標準のシグナルハンドラ(デフォルトシグナルハンドラ)が接続されている.開発者は基本的にこのシグナルハンドラの存在を気にする必要がない.シグナルハンドラはあるウィジェットのあるシグナルに対して一つだけ接続されているとは限らない.複数のシグナルハンドラが接続されている場合,シグナルハンドラが呼び出される順番は, 接続関数接続順に依存する.

接続関数

接続関数には,signal_connectとsignal_connect_afterがある.

GLib::Instantiatable.signal_connect(detailed_signal){|instance, *args| ...}
GLib::Instantiatable.signal_connect_after(detailed_signal){|instance, *args| ...}
  • signal_connectで接続されたシグナルハンドラは,デフォルトシグナルハンドラのに処理される.
  • signal_connect_afterで接続されたシグナルハンドラは,デフォルトシグナルハンドラのに処理される.
detailed_signal
シグナル名.指定可能なシグナルはウィジェットの種類に依存する.
instance
イベントが発生したウィジェット(Gtk::Widget).
*args
引数.イベントハンドラではこの引数を通じてイベント情報を取得できる.
返り値
ハンドラID.
signal_emit(detailed_signal, *args)
シグナルを発行.
これらの関数を使用した例を以下に示す.
#!/usr/bin/ruby

require 'gtk2'

win = Gtk::Window.new()
win.signal_connect("destroy") do
  Gtk.main_quit()
end

win.signal_connect_after("key_press_event") do
  puts "after"
end
win.signal_connect("key_press_event") do
  puts "before"
end

win.show()
Gtk.main()

呼び出し先の関数で呼び出されるように設定する関数をコールバック関数という.それに従うと,前述の関数らで記述されるブロックは,コールバックブロックといえる.

シグナルハンドラの接続と切断にはつぎの関数がある.

接続(connect)切断(disconnect)
GLib::Instantiatable.signal_connectGLib::Instantiatable.signal_handler_disconnect
GLib::Instantiatable.signal_connect_after
GLib::Instantiatable.signal_handler_disconnect(handler_id)
ハンドラID(handler_id)で指定したシグナルハンドラを切断(disconnect)する.
以下にsignal_handler_disconnectを使った場合の例を示す.
#!/usr/bin/ruby

require 'gtk2'

win = Gtk::Window.new()
win.signal_connect("destroy") do
  Gtk.main_quit()
end

id = win.signal_connect("key_press_event") do #接続
  puts "hoge"
end
win.signal_handler_disconnect(id) #切断

win.show()
Gtk.main()

接続されたシグナルハンドラを一時的に切断したいような場合,以下に示す関数を使用することで実装できる.

無効化(block)有効化(unblock)
GLib::Instantiatable.signal_handler_blockGLib::Instantiatable.signal_handler_unblock
#!/usr/bin/ruby

require 'gtk2'

win = Gtk::Window.new()
win.signal_connect("destroy") do
  Gtk.main_quit()
end

$sh = win.signal_connect("key_press_event") do |wdt, evt|
  puts "hoge"
  wdt.signal_handler_block($sh)
end

win.show()
Gtk.main()

接続順

同じ関数で接続されたシグナルハンドラは,接続された順番に処理される.それゆえ,次のようなプログラムを実行した時,"1st","2nd"の順で標準出力に出力される.

#!/usr/bin/ruby

require 'gtk2'

win = Gtk::Window.new()
win.signal_connect("destroy") do
  Gtk.main_quit()
end

win.signal_connect("key_press_event") do
  puts "1st"
end
win.signal_connect("key_press_event") do
  puts "2nd"
end

win.show()
Gtk.main()