I wrote several examples to show how to use TreeView as list or as tree. I hope this will help others to avoid ask the simple question again.
The purpose of these examples is to show the contents of certain directory.First, the simplest one, show the contents of the directory as a list. use Gtk2 '-init'; use Glib qw(TRUE FALSE); use Cwd; use constant { FILE_ICON => 0, FILE_NAME => 1, FILE_MTIME => 2, }; my $dir = shift || getcwd; unless ( -d $dir ) { die "$dir is not a directory or doesn't exist!\n"; } my $window = Gtk2::Window->new('toplevel'); $window->signal_connect('delete_event' => sub { Gtk2->main_quit; }); ret_vbox($window, $dir); $window->show(); Gtk2->main; sub ret_vbox { my $top = shift; my $dir = shift; $top->set_title('Directory contents of '. $dir); my $vbox = Gtk2::VBox->new(FALSE, 4); my $sw = Gtk2::ScrolledWindow->new; $sw->set_size_request (600, 400); $sw->set_policy ('automatic', 'automatic'); # create TreeView my $model = create_model($dir); my $treeview = Gtk2::TreeView->new_with_model($model); add_columns($treeview); $sw->add($treeview); $vbox->pack_start($sw,TRUE,TRUE,0); $vbox->show_all(); $top->add($vbox); } sub create_model { my $dir = shift; my $model = Gtk2::ListStore->new( 'Glib::String', 'Glib::String', 'Glib::String' ); opendir(DIR, $dir) or die "Can't open directory $dir: $!"; foreach my $file( readdir(DIR) ) { next if $file =~ /^[.]/; # ignore '.', '..' and hiden files my $iter = $model->append(); $model->set( $iter, FILE_ICON, ( -d "$dir/$file" ? 'gtk-directory' : 'gtk-file' ), FILE_NAME, $file, FILE_MTIME, scalar(localtime((stat("$dir/$file"))[9])) ); } return $model; } sub add_columns { my $treeview = shift; ## Add first column my $column1 = Gtk2::TreeViewColumn->new(); $column1->set_title('file name'); ### first cell for column one my $icon = Gtk2::CellRendererPixbuf->new(); $column1->pack_start($icon, FALSE); $column1->set_attributes( $icon, 'stock-id' => FILE_ICON ); ### second cell for column one my $name = Gtk2::CellRendererText->new(); $column1->pack_start($name, FALSE); $column1->set_attributes( $name, 'text' => FILE_NAME ); $column1->set_sort_column_id(FILE_NAME); $treeview->append_column($column1); ## Add second column my $column2 = Gtk2::TreeViewColumn->new(); $column2->set_title("modify time"); my $mtime = Gtk2::CellRendererText->new(); $column2->pack_start($mtime, FALSE); $column2->set_attributes($mtime, 'text' => FILE_MTIME); $column2->set_sort_column_id(FILE_MTIME); $treeview->append_column($column2); } These are standard operations to construct a TreeView: 1. create a TreeModel object using ListStore or TreeStore with several data types. Note that the columns of TreeModel may or may not appear in TreeViewColumn. 2. Create a TreeView using the TreeModel 3. Create one ore more TreeViewColumn and append them to the TreeView. For each TreeViewColumn, you can add one or more CellRender to it. Each CellRender can set attribute according to any column of TreeModel or set by a function. The third one also can be done by `insert_column_with_attributes' or `insert_column_with_data_func' if the column has only one cell. For example, to insert the second column, it can be rewrite as: ## Add second column $treeview->insert_column_with_attributes( 1, "modify time", Gtk2::CellRendererText->new(), 'text' => FILE_MTIME ); $treeview->get_column(1)->set_sort_column_id(FILE_MTIME); To sort the column by mtime, the TreeModel have to change, The FILE_MTIME should keep the mtime of the file as integer not the text. To show the date more readable, I use Date::Format::strftime to show the mtime: sub create_model { my $dir = shift; my $model = Gtk2::ListStore->new( 'Glib::String', 'Glib::String', 'Glib::Int' ); opendir(DIR, $dir) or die "Can't open directory $dir: $!"; foreach my $file ( readdir(DIR) ) { my $iter = $model->append(); $model->set( $iter, FILE_ICON, ( -d "$dir/$file" ? 'gtk-directory' : 'gtk-file' ), FILE_NAME, $file, FILE_MTIME, (stat("$dir/$file"))[9] ); } return $model; } sub add_columns { use Date::Format; my $treeview = shift; ## Add first column my $column1 = Gtk2::TreeViewColumn->new(); $column1->set_title('file name'); ### first cell for column one my $icon = Gtk2::CellRendererPixbuf->new(); $column1->pack_start($icon, FALSE); $column1->set_attributes( $icon, 'stock-id' => FILE_ICON ); ### second cell for column one my $name = Gtk2::CellRendererText->new(); $column1->pack_start($name, FALSE); $column1->set_attributes( $name, 'text' => FILE_NAME ); $column1->set_sort_column_id(FILE_NAME); $treeview->append_column($column1); ## Add second column $treeview->insert_column_with_data_func( 1, "modify time", Gtk2::CellRendererText->new(), sub { my ($tree_column, $cell, $model, $iter) = @_; my ($mtime) = $model->get ($iter, FILE_MTIME); my @lc = localtime($mtime); $cell->set (text => strftime("%c", @lc)); } ); $treeview->get_column(1)->set_sort_column_id(FILE_MTIME); } If we want display the contents of subdirectory as well, it is better to use TreeStore instead of ListStore. What we have to do, it to change the `create_model' functon: sub read_dir { my $model = shift; my $dir = shift; my $iter = shift; opendir(DIR, $dir) or die "Can't open directory $dir: $!"; foreach my $file ( readdir(DIR) ) { next if $file =~ /^[.]/; my $full = "$dir/$file"; my $iter_child = $model->append($iter); if ( -d $full ) { $model->set( $iter_child, FILE_ICON, 'gtk-directory', FILE_NAME, $file, FILE_MTIME, (stat("$dir/$file"))[9] ); read_dir($model, $full, $iter_child); } else { $model->set( $iter_child, FILE_ICON, 'gtk-file', FILE_NAME, $file, FILE_MTIME, (stat("$dir/$file"))[9] ); } } } sub create_model { my $dir = shift; my $model = Gtk2::TreeStore->new( 'Glib::String', 'Glib::String', 'Glib::Int' ); read_dir($model, $dir); return $model; } The contents of the directory will read into memory at start up. This may take quit a lot time if the directory contains many files. It is better to expand the directory when we want to. So the signal row-expanded is what we need. First we need add a column to TreeModel so that we can easy get the full file name of the TreeIter. use constant { FILE_ICON => 0, FILE_NAME => 1, FILE_MTIME => 2, }; The TreeModel is very simple now: sub create_model { my $dir = shift; my $model = Gtk2::TreeStore->new( 'Glib::String', 'Glib::String', 'Glib::String', 'Glib::Int' ); return $model; } Then we connect the signal and open the $dir in function `ret_vbox': $treeview->signal_connect( 'row-expanded' => \&expand_dir ); open_dir($model, $dir, undef); Add two new function: sub expand_dir { my ($treeview,$iter,$path,$self) = @_; my $model = $treeview->get_model(); my $dir = $model->get($iter, FILE_FULE_NAME); my $first = $model->iter_nth_child($iter, 0); unless ( defined $model->get($first, FILE_NAME) ) { open_dir($model, $dir, $iter); $model->remove($first); } return FALSE; } sub open_dir { my ($model, $dir, $parent_iter) = @_; opendir(DIR, $dir) or die "Can't open directory $dir: $!"; foreach my $file ( readdir(DIR) ) { next if $file =~ /^[.]/; my $full = "$dir/$file"; my $iter = $model->append($parent_iter); $model->set( $iter, FILE_ICON, ( -d $full ? 'gtk-directory' : 'gtk-file' ), FILE_NAME, $file, FILE_FULE_NAME, $full, FILE_MTIME, (stat($full))[9] ); if ( -d $full ) { my $dummy = $model->append($iter); } } my $dummy = $model->iter_nth_child($parent_iter, 0); unless ( $model->get($dummy, FILE_NAME) ) { $model->remove($dummy); } return $model; } If you want the file list looks better, you may want sort the contents by name but the directory always list first. What we need to do is to add an sort_func to the TreeModel. Add the following lines to the function `add_column': # Set customized sort funciton my $model = $treeview->get_model; $model->set_sort_func( FILE_NAME, sub { my ($model, $itera, $iterb) = @_; if ( $model->get($itera, FILE_NAME) && $model->get($iterb, FILE_NAME) && $model->get($itera, FILE_ICON) && $model->get($iterb, FILE_ICON) ) { $model->get($itera, FILE_ICON) cmp $model->get($iterb, FILE_ICON) || $model->get($itera, FILE_NAME) cmp $model->get($iterb, FILE_NAME); } }); $model->set_sort_column_id(FILE_NAME, $column1->get_sort_order); $model->sort_column_changed(); Ok, this is the final version(Encode module is used to support file name with multibyte characters): use Gtk2 '-init'; use Glib qw(TRUE FALSE); use Encode qw/encode decode/; use constant { FILE_ICON => 0, FILE_NAME => 1, FILE_FULE_NAME => 2, FILE_MTIME => 3, }; my $window = Gtk2::Window->new('toplevel'); $window->signal_connect('delete_event' => sub { Gtk2->main_quit; }); ret_vbox($window); $window->show(); Gtk2->main; sub ret_vbox { my $top = shift; my $dir = '.'; $top->set_title('Directory contents of '. $dir); my $vbox = Gtk2::VBox->new(FALSE, 4); my $sw = Gtk2::ScrolledWindow->new; $sw->set_size_request (600, 400); $sw->set_policy ('automatic', 'automatic'); # create TreeView my $model = create_model($dir); my $treeview = Gtk2::TreeView->new_with_model($model); open_dir($model, $dir, undef); add_columns($treeview); $treeview->signal_connect( 'row-expanded' => \&expand_dir ); $sw->add($treeview); $vbox->pack_start($sw,TRUE,TRUE,0); $vbox->show_all(); $top->add($vbox); } sub create_model { my $dir = shift; my $model = Gtk2::TreeStore->new( 'Glib::String', 'Glib::String', 'Glib::String', 'Glib::Int' ); return $model; } sub expand_dir { my ($treeview,$iter,$path,$self) = @_; my $model = $treeview->get_model(); my $dir = $model->get($iter, FILE_FULE_NAME); my $first = $model->iter_nth_child($iter, 0); unless ( defined $model->get($first, FILE_NAME) ) { open_dir($model, $dir, $iter); $model->remove($first); } return FALSE; } sub open_dir { my ($model, $dir, $parent_iter) = @_; opendir(DIR, $dir) or die "Can't open directory $dir: $!"; foreach my $file ( readdir(DIR) ) { next if $file =~ /^[.]/; my $uni_name = decode('utf8', $file); my $full = "$dir/$uni_name"; my $iter = $model->append($parent_iter); $model->set( $iter, FILE_ICON, ( -d $full ? 'gtk-directory' : 'gtk-file' ), FILE_NAME, $uni_name, FILE_FULE_NAME, $full, FILE_MTIME, (stat($full))[9] ); if ( -d $full ) { my $dummy = $model->append($iter); } } } sub add_columns { use Date::Format; my $treeview = shift; my $column; ## Add first column my $column1 = Gtk2::TreeViewColumn->new(); $column1->set_title('file name'); ### first cell for column one my $icon = Gtk2::CellRendererPixbuf->new(); $column1->pack_start($icon, FALSE); $column1->set_attributes( $icon, 'stock-id' => FILE_ICON ); ### second cell for column one my $name = Gtk2::CellRendererText->new(); $column1->pack_start($name, FALSE); $column1->set_attributes( $name, 'text' => FILE_NAME ); $column1->set_sort_column_id(FILE_NAME); # Set customized sort funciton my $model = $treeview->get_model; $model->set_sort_func( FILE_NAME, sub { my ($model, $itera, $iterb) = @_; if ( $model->get($itera, FILE_NAME) && $model->get($iterb, FILE_NAME) && $model->get($itera, FILE_ICON) && $model->get($iterb, FILE_ICON) ) { $model->get($itera, FILE_ICON) cmp $model->get($iterb, FILE_ICON) || $model->get($itera, FILE_NAME) cmp $model->get($iterb, FILE_NAME); } }); $model->set_sort_column_id(FILE_NAME, $column1->get_sort_order); $model->sort_column_changed(); $treeview->append_column($column1); ## Add second column $treeview->insert_column_with_data_func( 1, "modify time", Gtk2::CellRendererText->new(), sub { my ($tree_column, $cell, $model, $iter) = @_; my ($mtime) = $model->get ($iter, FILE_MTIME); my @lc = localtime($mtime); $cell->set (text => strftime("%c", @lc)); } ); $treeview->get_column(1)->set_sort_column_id(FILE_MTIME); } On Mon, 30 Jul 2007 22:18:30 +0800, muppet <[EMAIL PROTECTED]> wrote: > Well, "ugly" is subjective, so what you're saying is unclear. Are you > worried > that the icon will not indent with child nodes? Have you *tried* it? Sorry for my poor english. I know it is my fault, not gtk-perl. In fact, gtk-perl is powerful, and interesting, and I spent all the days to learn it. -- Best regards, Ye Wenbin _______________________________________________ gtk-perl-list mailing list gtk-perl-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-perl-list