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
取得可能な修飾キーを表す定数には次のようなものがある.
- SHIFT_MASK
- LOCK_MASK
- CONTROL_MASK
- MOD1_MASK
- MOD2_MASK
- MOD3_MASK
- MOD4_MASK
- MOD5_MASK
- BUTTON1_MASK
- BUTTON2_MASK
- BUTTON3_MASK
- BUTTON4_MASK
- BUTTON5_MASK
- RELEASE_MASK
- MODIFIER_MASK
接続
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.
#!/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_connect | GLib::Instantiatable.signal_handler_disconnect |
GLib::Instantiatable.signal_connect_after |
- GLib::Instantiatable.signal_handler_disconnect(handler_id)
- ハンドラID(handler_id)で指定したシグナルハンドラを切断(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_block | GLib::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()