ちょっと前の話になりますが、

From: akaishi / ruby.freak.ne.jp
Subject: [ruby-list:17956] Re: ruby-gtk の tree	オブジェクトに関する質問
Date: Sat, 23 Oct 1999 19:36:40 +0900

> Gtk::Tree は children がすべて remove されると self まで destroy されて
> しまうように見えます。そういう仕様?
> 
> 対処療法としては children をすべて remove せずに最低一つ残しておいて、何
> か append してから remove する、でしょうか。

昔、ディレクトリツリーを表示するクラスを作ったことがあって、参考までに添
付します。一応ディレクトリの増減に追従するようにしてみました。expand す
るときに更新します。unix 系専用。

--
akaishi

Gtk::DirTree<Gtk::Tree, Gtk::DirTreeItem<Gtk::TreeItem
ローカルなファイルシステムのディレクトリツリーを表示するためのウィジェッ
ト。

使い方:
dirtree = Gtk::DirTree.new		# DirTree ウィジェット生成
rootitem = Gtk::DirTreeItem.new('/')	# ルートとなる DirTreeItem 生成
					# 引数はパス名
dirtree.append rootitem
rootitem.show
dirtree.show

プログラム:

require 'gtk'

module Gtk

  class DirTreeItem < TreeItem
    attr_reader :path
    attr_writer :root

    def initialize(path)
      @path = path
      if path == '/'
	super '/'
      else
	if path[-1] == ?/ #/
	  path = path[0..-2]
	end
	super File.basename path
      end

      signal_connect('expand') {
	update_subtree
      }
    end

    def update_item
      if !@subtree and @root.walker.has_subdir?(path + '/')
	tree = Gtk::DirTree.new(@root)
	set_subtree tree
      end
    end

    def update_subtree
      newdirnames = @root.walker.find_dir(@path + '/').sort
      newdirsize = newdirnames.size
      del = []

      @subtree.children.each_with_index {|olditem, ind|
	olddirname = File.basename(olditem.path)
	while newdirnames[0] and newdirnames[0] < olddirname
	  if @path == '/'
	    newpath = '/' + newdirnames[0]
	  else
	    newpath = @path + '/' + newdirnames[0]
	  end
	  item = Gtk::DirTreeItem.new(newpath)
	  @subtree.insert item, ind
	  item.show
	  newdirnames.shift
	end

	if not newdirnames[0] or olddirname < newdirnames[0]
	  del.push olditem
	else
	  olditem.update_item
	  newdirnames.shift
	end
      }

      newdirnames.each {|dirname|
	if @path == '/'
	  newpath = '/' + dirname
	else
	  newpath = @path + '/' + dirname
	end
	item = Gtk::DirTreeItem.new newpath
	@subtree.append item
	item.show
      }

      del.each {|item| @subtree.remove item}
      @subtree = nil if newdirsize == 0
    end

    def set_subtree tree
      super tree
      @subtree = tree
    end

    def show
      super
      update_item
    end

  end

  class DirTree < Tree
    attr_accessor :walker

    def initialize root=nil, walker=nil
      super()
      if root
	@root = root
      else
	@root = self
	unless walker
	  @walker = DirWalker.new
	end
      end
    end

    def append item
      super
      item.root = @root
    end

    def insert item, pos
      super
      item.root = @root
    end

  end

  class DirWalker
    attr_reader :cwd

    def initialize
      @cwd = Dir.getwd
    end

    def root
      '/'
    end

    def find_dir(path)
      path += '/' unless path[-1] == ?/ #/
      res = []
      Dir.foreach(path) {|f|
	if f != '.' and f != '..' and File.directory? path + f
	  res.push f
	end
      }
      res
    end

    def has_subdir?(path)
      if File.stat(path).nlink == 2
	false
      else
	true
      end
    end

  end

end

if $0 == __FILE__
  window = Gtk::Window.new(Gtk::WINDOW_TOPLEVEL)
  swindow = Gtk::ScrolledWindow.new
  swindow.set_policy Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC

  dirtree = Gtk::DirTree.new
  rootitem = Gtk::DirTreeItem.new('/')
  dirtree.append rootitem
  rootitem.show
  dirtree.show

  swindow.add_with_viewport dirtree
  swindow.set_usize 200, 400
  swindow.show
  window.add swindow
  window.show
  Gtk.main()
end