Ruby-GNOME2の再帰メインループ (gkt2 3.1.8)

Gtk2の再帰メインループとDialog#runの関係が思ってたとおりではなかった。絶対忘れるのでメモ。

検証用コード

# -*- coding: utf-8 -*-
require 'gtk2'

window = Gtk::Window.new
window.signal_connect(:destroy){ Gtk.main_quit }

box = Gtk::VBox.new
window.add(box)

button_a = Gtk::Button.new('Gtk.main')
box.pack_start(button_a)
button_a.signal_connect(:clicked){
  dialog_open_gtk_main(window)
  false
}

button_b = Gtk::Button.new('Dialog#run')
box.pack_start(button_b)
button_b.signal_connect(:clicked){
  dialog_open_dialog_run(window)
  false
}

window.show_all

Gtk.quit_add(Gtk.main_level){
  puts 'quit!'
}

ml = nil
Gtk.idle_add{
  n = Gtk.main_level
  if n != ml
    puts "level #{ml.inspect} -> #{n.inspect}"
    ml = n
  end
  true
}

def dialog_open_gtk_main(parent)
  window2 = Gtk::Dialog.new('dialog', parent, Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT, [Gtk::Stock::OK, Gtk::Dialog::RESPONSE_ACCEPT])
  window2.signal_connect(:destroy){ Gtk.main_quit }
  window2.signal_connect(:response){ |w, r| window2.destroy }
  window2.show_all
  Gtk.main
  puts 'dialog_open_gtk_main: finish'
end

def dialog_open_dialog_run(parent)
  window2 = Gtk::Dialog.new('dialog', parent, Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT, [Gtk::Stock::OK, Gtk::Dialog::RESPONSE_ACCEPT])
  window2.run{ |r|
    window2.destroy
  }
  puts 'dialog_open_dialog_run: finish'
end
puts Gtk::BINDING_VERSION.join('.')
Gtk.main

同じようなダイアログボックスを、Gtk.main + イベントハンドリングで表示するのと Gtk::Dialog#run を使って表示するやつ。

出力

~/tmp $ ruby mainloop-test.rb 
3.1.8
level nil -> 1
dialog_open_dialog_run: finish
dialog_open_dialog_run: finish
level 1 -> 2
quit!
dialog_open_gtk_main: finish
level 2 -> 1
level 1 -> 2
dialog_open_gtk_main: finish
level 2 -> 1

わかったこと

Dialog#run は Blocks in a recursive main loop until the dialog either emits the response signal, or is destroyed. とのことだが再帰メインループレベルはインクリメントされない。当然、Gtk.quit_addで登録した再帰メインループ脱出時のハンドラも呼ばれない。ただし、Dialog#runで(ダイアログボックスに入力するまで)処理はブロックされ、再帰メインループのように扱うことが出来ている。