in reply to Directory tree explorer with stats reporting
Updated code follows to use File::Spec especially for those who are having problem on non-Unix operating systems; please test & report. Additionally, it binds 'q' to quit the Tk window, and displays the size in more appropriate units (see the BEGIN block).
Update (some minutes later): Explicitly use lstat to avoid the issue of circular|dangling links. It also reduces an extra stat call in alternate branch of if ( -d _ && ! -l _ ) { ... } else{ ... }.
Update (a few hours later): Move $tree->entryconfigure() outside of if ... else; suppress file & directory display if respective count is 0; removed variables which were used to calculate geometry of the window (which was dependent on font used|available and screen resolution (i think)).
Update (a few hours & some minutes later): Sort the enteries; move path stack joining in its own function; make the recursion, in buildSubTree(), to be in alternate branch of if ... else.
Update (next day's evening): Add key binding (enter|return key) to toggle collpase|expansion of a subtree; initially expand the tree of given directory w/ children collapsed; plus some otherwise minor changes.
Sep 19 2008 Update: Added key binding in help text.
#!perl # This is a modified copy of the directory explorer code posted on # PerlMonks by 'GrandFather' at ... # # http://perlmonks.org/index.pl?node_id=535607 # http://perlmonks.org/index.pl?displaytype=print;node_id=535607;repl +ies=1 use warnings; use strict; use Tk; use Tk::Tree; use Tk::Font; use File::Spec; BEGIN { # Tk-related variable(s). our ( $node_sep ) = ( '/' ); sub stack_as_string { return join $node_sep , ref $_[0] ? @{ $_[0] } : @_; } # Number of bytes. my %units = ( 1 => 'B' , 1024 => 'KB' , 1024 * 1024 => 'MB' , 1024 * 1024 * 1024 => 'GB' , 1024 * 1024 * 1024 * 1024 => 'TB' ) ; my @ordered = sort { $a <=> $b } keys %units; # Convert size in bytes to a unit (upto TB) appropriate for the ord +er of # the size. sub size_in_xbyte { my ( $size ) = @_; return 'UNKNOWN SIZE' unless defined $size; my $factor = $ordered[0]; foreach my $u ( @ordered ) { $size < $u and last; $factor = $u; } # Use number of bytes (B) as is (knowing that $factor will be 1). my $format = $factor != $ordered[0] ? '%0.1f %s' : '%0d %s'; return sprintf $format, $size / $factor, $units{$factor}; } } our ( $node_sep ); my $tree_start = 1; my $path = shift; ShowHelp() unless defined $path; # In File::Spec pod, there is no constructor method noted, nor is 'si +mple # use' defined for which functional forms of methods are available. +Missing # also is a list of exported function. my $fspec = 'File::Spec'; $path = $fspec->canonpath($path); ShowHelp( -2 , "Error finding folder $path\n\n" ) unless -d $path; my $main = MainWindow->new( '-title' => "Folder stats for $path" ); # Adjust as you like. $main->geometry( '900x700' ); my $tree = $main->ScrlTree ( '-font' => $main->Font( 'systemfont' ) , '-itemtype' => 'text' , '-separator' => $node_sep # Once, having scrollbars-only-when-needed-option, 'o', did not m +ake the # scrollbars appear when the content overflows display area (Free +BSD # 6-STABLE & Tk-804.027, Tk 8.4.11.2). Now (Sun Mar 12 13:18:18 U +TC 2006) # optional scrollbars are behaving as advertised. And i do not k +now # why|how! , '-scrollbars' => 'osow' ) ->pack( '-fill' => 'both' , '-expand' => 1 ) ; my @node_stack = ($tree_start); my $node = $tree_start; $tree->add( $node , '-text' => $path ); my ( $subDirCount , $subFileCount , $subTotalSize ) = buildSubTree( $tree , $path , \@node_stack ); $tree->entryconfigure ( $node , '-text' => annotate( $path , $subTotalSize , $subDirCount , $subFile +Count ) ); $tree->autosetmode; collapse_nodes( $tree ); $tree->open( $node ); $main->bind( '<KeyPress-q>' , sub { exit ; } ); $tree->bind( '<KeyPress-Return>' , \&toggle_tree_expand ); $tree->focus; MainLoop; # Key binding function to expand|collapse a subtree. sub toggle_tree_expand { my $root = $tree->info( 'selection' ); return unless defined $root && length $root; my @nodes = $tree->info( 'children' , $root ); return unless scalar @nodes; # $tree->open() does not (sometimes) expands a tree, but $tree->clo +se() does # collapse an open()'d tree. # -- # Handle both open & close cases for now. my ( $meth ); foreach ( @nodes ) { $meth = $tree->info( 'hidden' , $nodes[-1] ) ? 'show' : 'hide'; $tree->setmode( $root , $meth eq 'show' ? 'close' : 'open' ); $tree->$meth( 'entry' , $_ ); } $tree->update; } sub collapse_nodes { my ( $tree , $node , $collapse ) = @_; my @subnodes = $tree->info( 'children' , $node ); return unless scalar @subnodes; foreach my $n ( @subnodes ) { collapse_nodes( $tree , $n , 1 ); $tree->hide( 'entry' , $n ) if $collapse; } $tree->setmode( $node , 'open' ) if $node; } sub buildSubTree { my ( $tree , $path , $stack ) = @_ ; my ( $dirCount , $fileCount , $sizeTotal ) = (0) x3 ; push @$stack , $tree_start; opendir my $DH , $path or die "Cannot open $path: $!\n"; foreach my $dir ( sort readdir $DH ) { next if $dir eq $fspec->updir or $dir eq $fspec->curdir; # Don't know what will happen if catfile() is substitued w/ catdi +r() # on non-Unix operating systems. my $path = $fspec->catfile( $path , $dir ); my $node = stack_as_string( $stack ); $tree->add( $node , '-text' => $path ); my ( $size , $dirs , $files ); # Use lstat to avoid chasing circular|dangling symbolic links. lstat $path; if ( ! -d _ ) { ++$fileCount; $size = -s _; $sizeTotal += $size if defined $size; } else { ( $dirs , $files , $size ) = buildSubTree( $tree , $path , $stac +k ); $dirCount += $dirs + 1; $fileCount += $files; $sizeTotal += $size; } $tree->entryconfigure ( $node , '-text' => annotate( $path , $size , $dirs , $files ) ); ++$stack->[-1]; } closedir $DH or die "Cannot close $path: $!\n"; pop @$stack; return ( $dirCount , $fileCount , $sizeTotal ); } sub annotate { my ( $path , $size , $dirs , $files ) = @_; return join q// , $path , "\t(" , size_in_xbyte( $size ) , ( map { !$_->[0] ? () : sprintf ', %s %s%s' , @{$_} , count_to_plural_suffix( $_-> +[0] ) } [ $dirs , 'dir' ] , [ $files , 'file' ] ) , ')' ; } sub count_to_plural_suffix { return $_[0] > 1 ? 's' : ''; } sub ShowHelp { my $exitValue = 0; $exitValue = shift if defined $_[0] and $_[0] =~ /^[-+]?\d+$/; print $_ while $_ = shift; print <<HELP; FolderStats scans a directory tree starting at the folder given on the command line and generates an explorer like tree giving folder content stats such as number of files, their total size, and the count and sizes of sub-folders. Note that the statistics are not dynamically updated as files and folders are altered on disk. Usage: FolderStats <root folder> Key Binding: q: quits the program. Enter: expands or collapses a tree. Up, Down: Vertically move up or down. arrows: Left, Right: Move one level up or down. arrows: HELP exit( $exitValue || -1 ); }
|
---|
Replies are listed 'Best First'. | |
---|---|
Re^2: Directory tree explorer with stats reporting
by GrandFather (Saint) on Mar 12, 2006 at 01:58 UTC | |
by parv (Parson) on Mar 12, 2006 at 02:45 UTC | |
by GrandFather (Saint) on Mar 12, 2006 at 03:06 UTC |