http://www.perlmonks.org?node_id=59392
Category: GUI Programming
Author/Contact Info Theo Petersen theopetersen@yahoo.com
Description: Show module versions for a running program.

I wrote this window to help with debugging mysterious failures at customer sites. We send scripts and modules to our users, who install them locally. They also install updates from CPAN as recommended, but we sometimes find we are out of sync. This code can be added to any Perl/Tk program to bring up a window which displays the versions of all modules loaded by the program to that point. The information is displayed in a Text widget, which allows the user to cut and paste it easily into a mail message.

The enclosed wrapper program is trivial but serves as an example framework. I cut and pasted it from one of our apps (and noticed the old-style Frame menu bar along the way, hmmm). On re-reading I think the code that gets the version data is rather clunky; any suggestions?

#!/usr/bin/perl -w

use strict;
use vars qw($VERSION $aboutWindow $mainWindow $camelanim);
use Tk;

$VERSION = "0.01";

# Make the main window, menu bar and Help menu.
$mainWindow = new MainWindow (-title => "A program",);
my $mb = $mainWindow->Frame(-relief => 'raised', -borderwidth => 2);
$mb->pack(
          -fill => 'x',
          -expand => 'false',
         );
my $helpB = $mb->Menubutton(
                            -text => 'Help',
                            -underline => 0,
                           );
$helpB->menu(-tearoff => 0);
$helpB->pack(-side => 'right');
$helpB->command(
                -label => 'About...',
                -underline => 0,
                -command => \&aboutViewer,
               );
MainLoop;

# Show module information.
sub aboutViewer {
    require Tk::Animation;
    require Tk::ROText;
    if ($aboutWindow) {
        $aboutWindow->deiconify;
        $aboutWindow->focus;
        $camelanim->start_animation(200);
    }
    $aboutWindow = $mainWindow->Toplevel(-title => 'About Viewer');
    my $top = $aboutWindow->Frame->pack(-fill => 'x', -expand => 'true
+',);
    my $right = $top->Frame->pack(-side => 'right');
    my $left = $top->Frame->pack(-side => 'left');
    $left->Message(
                  -text => "Copyright (C) 2001 by Your Name Here",
                  -relief => 'flat',
                  -width => 400,
                 )->pack(-fill => 'x', -expand => 'true',);
    $camelanim = $right->Animation(
                                   '-format' => 'gif',
                                   -file => Tk->findINC('anim.gif')
                                  ) unless $camelanim;
    $right->Label(-image => $camelanim)->pack;
    $camelanim->start_animation(200);
    my $aboutText = $aboutWindow->Scrolled(
                                           'ROText',
                                           -scrollbars => 'oe',
                                           -wrap => 'word',
                                           -width => 40,
                                           -height => 20,
                                          )->pack(
                                                  -fill => 'x',
                                                  -expand => 'true',
                                                 );
    $aboutText->insert('end',"$0 $::VERSION\n") if defined($::VERSION)
+;
    foreach (&moduleDump) {
        $aboutText->insert('end',$_);
    }
    $aboutWindow->Button(Name => 'okayButton',
                         -text => 'Okay',
                         -command => sub { $aboutWindow->withdraw; },
                        )->pack(-side => 'bottom');

    $aboutWindow->bind(
                       '<Destroy>',
                       sub {
                           undef($aboutWindow);
                           $camelanim->stop_animation;
                       }
                      );
    $aboutWindow->bind(
                       '<Unmap>',
                       sub {
                           $camelanim->stop_animation;
                       }
                      );
}

# Roll credits.
sub moduleDump {
    my ($module, $line, @out, %package_stab, $package_version);
    foreach (sort(keys %INC)) {
    next unless /^[A-Z]/;
    $module = $_;
    $module =~ s/\.pm$//;
    $module =~ s/\//::/g;

    %package_stab = eval("%{*" . $module . "::}");
    if (exists $package_stab{'VERSION'}) {
        $package_version = $package_stab{'VERSION'};
        $package_version = $$package_version;
        if (defined($package_version)) {
        $line = $module . " version $package_version\n";
        }
        else {
        $line = $module . ", no version\n";
        }
    }
    else {
        $line = $module . ", no version\n";
    }
    push @out, $line;
    }
    return @out;
}