- 论坛徽章:
- 1
|
Gtk2-Perl 7.TreeView
TreeView 用于显示存储在 Gtk2::TreeModel 对象中的数据。两种主要的 model 类:Gtk2::TreeStore 和 Gtk2::ListStore。
下面这个例子可以显示当前目录下的文件内容:
#!/usr/bin/perl -w
# treeview1.pl --- Simple TreeView
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;
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);
}
源程序
创建一个树的过程可以归纳为这样一个过程:
1. 创建树的 model
2. 使用这个 model 新建一个树
3. 加入树的 column,每个 column 可以有多个 cell,每个 cell 都有不同的属性,通过设置属性来控制每个 column 里显示的内容。
显示列表里每一行对应 model 里每一个 TreeIter。
有时候可以用 insert_column_with_attributes 简化插入一个 column 的过程,比如前面第二个 column 可以换成下面这样,append_column 那一行就不需要了:
## 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);
但是用 insert_column_with_attributes 后就不能在同一栏里插入两个 cell,所以 column1 还是要用前面这种写法。
TreeViewColumn 里显示的数据也可以不在 TreeModel 里。可以设置函数来控制每一个 TreeIter 在每一栏中应该显示什么内容。这可以用 TreeView 的 insert_column_with_data_func 或者 set_cell_data_func 函数实现。比如前面这个例子里,如果要让文件按时间排序,用 localtime 转换后的字符串是不行的。所以要改成这样:
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) ) {
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, (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);
}
源程序
前面是关于使用 ListStore 的使用,TreeStore 的使用和 ListStore 是很类似的。只是创建 model 的方法不同而已。对于静态数据只要修改前面的 create_model 函数就行了:
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;
}
源程序
在前面这个例子递归把目录下的文件加到 TreeStore 里。使用 append 函数向一个 TreeIter 加入一个子项并返回这个子项。由于是树在显示之前就读入全部的数据,所以在目录里文件较多时会明显的感觉变慢了。如果要动态展开树,需要使用 row-expanded 这个信号。这是一个动态显示文件系统树的例子。
#!/usr/bin/perl -w
# treeview4.pl ---
use Gtk2 '-init';
use Glib qw(TRUE FALSE);
use Cwd;
use constant {
FILE_ICON => 0,
FILE_NAME => 1,
FILE_FULE_NAME => 2,
FILE_MTIME => 3,
};
my $dir = shift || getcwd;
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);
$treeview->signal_connect( 'row-expanded' => \&expand_dir );
open_dir($model, $dir, undef);
$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 $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);
}
}
return $model;
}
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);
$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);
}
源程序
这样一个显示文件的树基本实现了。考虑到 Gtk2 使用的字符串和 perl 内部字符串相同,所以在读入目录时文件名中有多字节字符时会有问题,需要改成这样:
sub open_dir {
use Encode qw/encode decode/;
my ($model, $dir, $parent_iter) = @_;
opendir(DIR, $dir) or die "Can't open directory $dir: $!";
foreach my $file ( readdir(DIR) ) {
next if $file =~ /^[.]/;
my $enc_name = decode('utf8', $file);
my $full = "$dir/$enc_name";
my $iter = $model->append($parent_iter);
$model->set(
$iter,
FILE_ICON, ( -d $full ? 'gtk-directory' : 'gtk-file' ),
FILE_NAME, $enc_name,
FILE_FULE_NAME, $full,
FILE_MTIME, (stat($full))[9]
);
if ( -d $full ) {
my $dummy = $model->append($iter);
}
}
}
但是还有一个不足之处是,这里排序时不会优先考虑文件夹。让我们再来实现这个功能。在 add_columns 这个函数 $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();
源程序 |
|