[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: image layout questions



On Tue, 7 Aug 2007 19:03:04 +1200
"Johan Aberg" <abergster gmail com> wrote:

>I'm looking for a way to display images in a horizontal layout. I'm
>building a timeline displaying images from left to right. I'd like to
>be able to use set_reorderable(TRUE) to shuffle the images around left
>or right on this timeline.
>The TreeView seems to be a good candidate of doing this, but it seems
>to be design to show data in rows and columns in a vertical layout. I
>only want one row with lots of columns and I need to be able to access
>each cell to reorder them.
>Any suggestion what widget/class that could help me out here?

I wrote this thumbnail-previwer awhile ago. It uses the Gnome2::Canvas
for display. I display the thumbnails vertically, but you could
easily switch it to horizontal. Reordering them out to be easy too.
Anyways, just run the script in a directory
full of images.... it will find all images and recurse into sub-dirs.
As far as widgets go, for doing specialized things of your imagination,
it's hard to beat a Canvas.

#!/usr/bin/perl
use warnings;
use strict;
use Gtk2 '-init';
use Glib qw/TRUE FALSE/; 
use Gnome2::Canvas;


chdir '..';  #little hack to load any images in toplevel directory
             #otherwise only subdirs are shown at opening
	                   
#automatically put full file path to clipboards
#to paste menu
my $clipboard =  Gtk2::Clipboard->get(Gtk2::Gdk->SELECTION_CLIPBOARD);
#to mouse
my $clipboard1 =  Gtk2::Clipboard->get(Gtk2::Gdk->SELECTION_PRIMARY);

my $window = Gtk2::Window->new('toplevel');
$window->signal_connect('delete_event' => sub { Gtk2->main_quit; });
$window->set_border_width(5);
$window->set_default_size(600,400);

my $vbox = Gtk2::VBox->new(0,1);

#create a hbox to pack the information label
   my $hbox0 = Gtk2::VBox->new(FALSE,1);
   my $label_info = Gtk2::Label->new();
   $label_info->set_markup( &make_label('Filename','Dimensions','Size'));

 $hbox0->pack_start($label_info,FALSE,FALSE,1);	
 $vbox->pack_start($hbox0,0,0,0);

my $hbox = Gtk2::HBox->new(0,5);
#these vboxs will return the bulk of the gui
my $tbox = &ret_tree();  #the dir selector
$hbox->pack_start($tbox,0,0,0);

my ($cbox,$can) = &ret_can(); #thumbnail canvas
$hbox->pack_start($cbox,0,0,0);

my ($zbox,$can1) = &ret_can1(); #main image canvas
$hbox->pack_start($zbox,1,1,1);

$vbox->pack_start($hbox,1,1,0);

#add and show the vbox
$window->add($vbox);
$window->show_all();

#our main event-loop
Gtk2->main();
###################################################################
sub make_label{
my ($name, $dimensions, $size) = @_;
return  
"<span background = 'yellow' foreground= 'black' size ='10000'><i> $name  </i></span>".
"<span background = 'black' foreground= 'green' size ='20000'><i> $dimensions  </i></span>".
"<span background = 'blue' foreground= 'red' size ='20000'><i> $size  </i></span>";
}
#################################################################
sub ret_tree {
my $vbox = Gtk2::VBox->new(FALSE,5);
	#create a scrolled window that will host the treeview
	my $sw = Gtk2::ScrolledWindow->new (undef, undef);
    		$sw->set_shadow_type ('etched-out');
		$sw->set_policy ('automatic','always');
		$sw->set_placement('top-right');
		$sw->set_size_request (200, 300);
        	$sw->set_border_width(0);

       	my $tree_store = Gtk2::TreeStore->new(qw/Glib::String/);

        my %tree = %{ hashdir() };

	#fill it with data
	foreach my $key (sort keys %tree ) {
		#the iter is a pointer in the treestore. We
		#use to add data.
                my $iter = $tree_store->append(undef);
		$tree_store->set ($iter,0 => $key);
		
                #need recursive sub here to nest subdirs
                &recurse($tree_store, $tree{$key} , $iter );
         }

            #this will create a treeview, specify $tree_store as its model
	    my $tree_view = Gtk2::TreeView->new($tree_store);
		
	    #create a Gtk2::TreeViewColumn to add to $tree_view
	    my $tree_column = Gtk2::TreeViewColumn->new();
		
	    $tree_column->set_title('Select');
			
	   #create a renderer that will be used to display info
	   #in the model
	   my $renderer = Gtk2::CellRendererText->new;
	   #add this renderer to $tree_column. This works like a Gtk2::Hbox
	   # so you can add more than one renderer to $tree_column			
	   $tree_column->pack_start ($renderer, FALSE);
		
	   # set the cell "text" attribute to column 0   
	   #- retrieve text from that column in treestore 
	   # Thus, the "text" attribute's value will depend on the row's value
	   # of column 0 in the model($treestore),
	   # and this will be displayed by $renderer,
	   # which is a text renderer
	  $tree_column->add_attribute($renderer, text => 0);
	
	   #add $tree_column to the treeview
	  $tree_view->append_column ($tree_column);
	
	 $sw->add($tree_view);

         $tree_view->get_selection->signal_connect(
                changed =>\&cell_selected,$tree_store ); 

         #$tree_view->expand_all;
	 # expand only first level
	 $tree_view->expand_row (Gtk2::TreePath->new(0),0);
         
$vbox->pack_start($sw,1,1,0);
$vbox->show_all();
return $vbox;
}
######################################################################
sub ret_can {
 
  my $vbox = Gtk2::VBox->new(FALSE,5);
	#create a scrolled window that will host the treeview
	my $sw = Gtk2::ScrolledWindow->new (undef, undef);
    		$sw->set_shadow_type ('etched-out');
		$sw->set_policy ('automatic', 'always');
		$sw->set_placement('top-right');
		$sw->set_size_request (130, 300);
        	$sw->set_border_width(0);

        my $canvas   = Gnome2::Canvas->new_aa;
        $canvas->set_scroll_region( 0,0,10,2000);

        my $black = Gtk2::Gdk::Color->new (0x0000,0x0000,0x0000);
        $canvas->modify_bg('normal',$black);
	$canvas->set_center_scroll_region (FALSE);
  
        $sw->add($canvas);

   $vbox->pack_start($sw,TRUE,TRUE,0);
   $vbox->show_all();
return ($vbox,$canvas);
}
######################################################################
sub ret_can1{
 
  my $vbox = Gtk2::VBox->new(TRUE,5);
	#create a scrolled window that will host the thumb canvas
	my $sw = Gtk2::ScrolledWindow->new (undef, undef);
    		$sw->set_shadow_type ('etched-out');
		$sw->set_policy ('always', 'always');
		$sw->set_placement('top-right');
		$sw->set_size_request (300, 300);
        	$sw->set_border_width(0);

        my $canvas   = Gnome2::Canvas->new_aa;
        $canvas->set_scroll_region( 0,0,2000,2000);

        my $black = Gtk2::Gdk::Color->new (0x0000,0x0000,0x0000);
        $canvas->modify_bg('normal',$black);
	$canvas->set_center_scroll_region (FALSE);

    $sw->add($canvas);

$vbox->pack_start($sw,TRUE,TRUE,0);
$vbox->show_all();
return ($vbox,$canvas);
}
######################################################################
sub recurse{            
  my ( $tree_store, $hashref , $iter ) = @_;
  
     my %hash = %{$hashref};
  
     foreach my $key (sort keys %hash ){	
        
       my $iter_child = $tree_store->append($iter);                    
       if( scalar (keys %hash == 0 ) ){  
	     $tree_store->set ($iter_child,0 => $key );
	 }else{
	    $tree_store->set ($iter_child,0 => $key );
	   &recurse($tree_store, $hash{$key}, $iter_child);
	 }
   }
}
###################################################################

sub cell_selected{
       my ($tree_selection,$model ) = @_ ;

       my $sel = $tree_selection->get_selected_rows;
       my $value='';
       if( defined $sel ){
          my $path = $sel->to_string;
          my @path_ele = split /:/, $path;
        #reconstruct filesystem path from model path         
	 while( @path_ele ){
	    $path = join ':', @path_ele;
	    #print "path $path\n";
	    my $iter = $model->get_iter_from_string($path);
	    my $val = $model->get($iter,0);
	    $value = $val.'/'.$value;
	    pop @path_ele;
	   }
      }  
#print "$value\n";
add_dir_contents("./$value");

return FALSE;
}
#############################################################

sub hashdir {
    my $dir = shift || '.';
    opendir my $dh, $dir or die $!;

    my $tree = {}->{$dir} = {};

    while ( my $file = readdir($dh) ) {
        next if $file =~ m[^\.{1,2}$];

         my $path = $dir . '/' . $file;

      if(-d $path){ 
       $tree->{$file} = hashdir($path); 
      }else{ next }
    }

return $tree;
}
#########################################################
sub add_dir_contents {
    my $path   = $_[0];

  #this decode utf8 routine is used so filenames with extended
   # ascii characters (unicode) in filenames, will work properly
   use Encode;
   opendir my $dh, $path or warn "Error: $!";
   my @files = grep !/^\.\.?$/, readdir $dh;
   closedir $dh;
   @files = map { decode( 'utf8', "$path/".$_  ) } sort @files;
     
    my @thumbs=();
    
    foreach my $file (@files) {
        $file =~ s|//|/|g;
       (my $text = $file ) =~ s|^.*/||g;
      if( $file =~ /.*\.(png|jpg|gif)$/ ){ push @thumbs, $file }
    }

#print "@thumbs\n";
load_thumbs( \ thumbs );

}
###############################################################
sub load_thumbs{
my $thumbsref = shift; 

#clean out old thumbs 
foreach my $item ( @{$can->{'temp'}} ){ $item->destroy; }
@{$can->{'temp'}} =();

my $root = $can->root;
my $count = 0;

foreach my $file( @$thumbsref ){
        
        my $pixbuf_t = Gtk2::Gdk::Pixbuf->new_from_file_at_scale($file,100,100,1);
	my $image = Gnome2::Canvas::Item->new ($root,
					       'Gnome2::Canvas::Pixbuf',
					       pixbuf => $pixbuf_t,
					       x      => 2.0,
					       y      => $count * 108,
					       width  => 100,
					       height => $pixbuf_t->get_height,
					       anchor => 'nw',
					       );
       
        $image->{'filepath'} = $file; #data rider to hold path

        $image->signal_connect (event => sub {
                my ($item, $event) = @_;
		if( $event->type eq 'button-press' ){
		     &display_image( $image->{'filepath'}  );
		   }
         });
         
       my $line = Gnome2::Canvas::Item->new ($root,
	       'Gnome2::Canvas::Line',
	        points => [0.0, $count * 108, 130.0, $count * 108],
            fill_color => '#ff0000',
            width_units => 6.0,
       );


   push @{$can->{'temp'}}, $image; #list to delete on refresh
   push @{$can->{'temp'}}, $line;
   $count++;
 }
#print @{$can->{'temp'}},"\n";
#display_image( ${$can->{'temp'}}[0]  );  #display first image in list
$can->set_scroll_region (0, 0, 10, $count * 108);
$can->scroll_to (0, 0); 
}
##############################################################
sub display_image{
  if( ref $can1->{'temp'} eq 'Gnome2::Canvas::Pixbuf' ){
                  $can1->{'temp'}->destroy}

  my $im = Gtk2::Gdk::Pixbuf->new_from_file( $_[0] );
  my $x = $im->get_width;
  my $y = $im->get_height;
  my $image = Gnome2::Canvas::Item->new ($can1->root,
				       'Gnome2::Canvas::Pixbuf',
                                           pixbuf => $im,
					   x      => 5.0,
					   y      => 5.0,
					   width  => $x,
					   height => $y,
					   anchor => 'nw',
					   );

 $can1->{'temp'} = $image;
 $can1->set_scroll_region (0, 0, $x + 10, $y + 10);
 $can1->scroll_to (0, 0); 

 # set to clipboard for menu or mouse paste
 $clipboard->clear;
 $clipboard1->clear;
 $clipboard->set_text($_[0]);
 $clipboard1->set_text($_[0]);

 my $filebase = substr ($_[0], rindex ($_[0], "/") + 1);
 $label_info->set_markup( &make_label( $filebase, "$x x $y" , (stat $_[0] )[7] ));
}
##################################################################

__END__
zentara
-- 
I'm not really a human, but I play one on earth.
http://zentara.net/japh.html



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]