Mini Image Demo

From WxPerl Wiki
Jump to: navigation, search

This is a fixed up version of the code from the Perl Monks #3 tutorial by Boo Radley. The original wouldn't run on recent wxPerl versions. Changes required:

  • I removed use 'Wx::Image' this is now included by 'use Wx'
  • I moved the initial picture filename to the top of the file
  • I changed the ConvertToBitmap method (not only deprecated but removed) to Wx::Bitmap->new( $image )
  • I commented out $parent->Clear in OnChange()
#!/usr/bin/perl

use Wx ;

my $filename = "c:/docume~1/porterje/wx-ex.jpg";

package MyApp;
    use strict;
    use vars qw(@ISA);
    use Wx qw(:everything);
    use Wx::Event qw(EVT_MENU);
    @ISA=qw(Wx::App);

    sub OnInit {
      my $this = @_;
      my $frame = MyFrame->new( "Mini-image demo", [-1,-1], [-1,-1]);
      #my $this->{FRAME}=$frame;
      unless ($frame) {print "unable to create frame -- exiting."; return undef}
      $frame->Show( 1 );
      1;
    }

package MyFrame;
    use vars qw(@ISA);
    use strict;
    #
    #   All we load here are constants used
    #   to keep the image stretched to the dimensions of the window.
    #
    use Wx qw(wxWidth wxHeight wxLeft wxTop wxDefaultPosition wxDefaultSize
              wxID_CANCEL wxCentreX wxCentreY);
    use Wx::Event qw(:everything);
    #
    #   Wx::Image loads the Image control and all of the Image handlers.
    #
    use IO::File;
    use Wx::Event ;
    @ISA=qw(Wx::Frame);

    sub new {
        my $class = shift;
        my $this = $class->SUPER::new( undef, -1, $_[0], $_[1], $_[2] );
        #
        #   replace the filename with something appropriate.
        #
        my $file = IO::File->new( $filename, "r" );
        unless ($file) {print "Can't load $filename.";return undef};
        binmode $file;
        my $handler = Wx::JPEGHandler->new();
        my $image = Wx::Image->new();
        my $bmp;    # used to hold the bitmap.
        $handler->LoadFile( $image, $file );
        $bmp = Wx::Bitmap->new($image); 

        if( $bmp->Ok() ) {
            #  create a static bitmap called ImageViewer that displays the
            #  selected image.
            $this->{ImageViewer}= Wx::StaticBitmap->new($this, -1, $bmp);
        }
        $this->{ScaleImage}=0;
        $this->{TreeCtrl}= MyTreeCtrl->new($this, -1);

        $this->SetAutoLayout( 1 );  # allow wxperl to manage control sizing & placement
        # Layout constraints provide the guides
        # for wxperl's autolayout.
        my $b1 = Wx::LayoutConstraints->new();
        my $b2 = Wx::LayoutConstraints->new();

        # These constrainst define the placement and
        # dimensions of the controls they're bound to,
        # and can be either absolute, or relative to
        # other controls
        $b1->left->Absolute(0);
        $b1->top->Absolute(0);
        $b1->width->PercentOf( $this, wxWidth,50);
        $b1->height->PercentOf( $this, wxHeight, 100);
        $this->{TreeCtrl}->SetConstraints($b1);

        $b2->left->RightOf($this->{TreeCtrl});
        $b2->top->Absolute(0);
        $b2->width->PercentOf( $this, wxWidth,50);
        $b2->height->PercentOf( $this, wxHeight, 100);
        $this->{ImageViewer}->SetConstraints($b2);

        #
        # Set up the menu bar.
        #
        my $file_menu = Wx::Menu->new();
        my ($OPEN_NEW_DIR, $REMOVE_DIR, $SCALE_IMAGE, $APP_QUIT)=(1..100);
        $file_menu-> Append( $OPEN_NEW_DIR, "&Open A Directory\tCtrl-O");
        $file_menu->AppendSeparator();
        $file_menu->Append($SCALE_IMAGE,"&Scale Images To Window\tCtrl-S","",1);
        $file_menu->AppendSeparator();
        $file_menu->Append ($APP_QUIT, "E&xit\tCtrl-x","Exit Application");
        #
        # Note that even though there are 6 options, only
        # 4 of them are active as they're the only ones
        # bound to event handlers.
        #
        EVT_MENU($this, $OPEN_NEW_DIR, \&OnDirDialog);
        EVT_MENU($this, $SCALE_IMAGE,  \&Set_Scale);
        EVT_MENU($this, $APP_QUIT,     sub {$_[0]->Close( 1 )});

        my $menubar= Wx::MenuBar->new();
        $menubar->Append ($file_menu, "&File");
        $this->SetMenuBar($menubar);

        $this;  # return the frame object to the calling application.
    }

    # Set_Scale changes a scalar flag which will determine
    # if a displayed image is resized to fit the full
    # dimensions of the image control.
    sub Set_Scale {
        my $this = shift;
        # yes, I could have used NOT here.
        if ($this->{ScaleImage}){$this->{ScaleImage}=0} else {$this->{ScaleImage}=1};
    }

    #
    #   OnDirDialog scans a user specified directory for images.
    #
    sub OnDirDialog {
        my( $this, $event ) = @_;

        my $dialog = Wx::DirDialog->new( $this );

        unless ( $dialog->ShowModal == wxID_CANCEL ) {
            $this->{TreeCtrl}->ScanDir($dialog->GetPath);
        }
        $dialog->Destroy;
    }

package MyTreeCtrl;
    use vars qw (@ISA);
    use strict;
    use Wx qw(:everything);
    use Wx::Event qw(EVT_TREE_SEL_CHANGED EVT_TREE_ITEM_ACTIVATED) ;
    use Win32;
    use HTML::SimpleLinkExtor;
    use LWP::MediaTypes qw(guess_media_type);
    use LWP::UserAgent;
    @ISA=qw(Wx::TreeCtrl);

sub new {
        my $class = shift;
        my $this = $class->SUPER::new( @_  );
        EVT_TREE_SEL_CHANGED ($this,$this, \&OnChange);
        my $root=$this->AddRoot("Image listings"); #add one default item.

        $this;
}
#
# Scandir adds a child node to the treeview that
# lists the directory, and then sets each image file to
# be a child of that newly created node.
#
sub ScanDir {
    my $this=shift;
    my $dir=shift;
    $dir =~tr/\\/\//;
    if ($dir !~/[\/]$/) {$dir.="/"}
    $dir=Win32::GetShortPathName($dir);
    my (@files) =glob "$dir*.jpg";
    push @files, glob "$dir*.gif";
    my $item = $this->AppendItem ($this->GetRootItem, "$dir (".scalar @files.") files");
    foreach (@files) {$this->AppendItem ($item,"$_")}
}

#
# event handler for selecting different images.
#
sub OnChange {
    my ($this, $event) =@_;
    my $item = $event->GetItem();
    if ($item== $this->GetRootItem()) {return} # return if user clicked on the root node.

    my $txt = $this->GetItemText($item);
    my $ext;
    my $handler;
    $txt=reverse $txt;
    if ($txt=~/^(.*?)\./){
        $ext=lc(reverse($1));
    }
    $txt=reverse ($txt);
    # this is cheesy, but the autosensing handler doesn't work,
    # or I can't get it to work appropriately.
    if ($ext eq "jpg"){$handler = Wx::JPEGHandler->new()};
    if ($ext eq "gif"){$handler = Wx::GIFHandler->new()};
    # create file
    my $file;
    $file = IO::File->new( $txt, "r" ) or return undef;
    binmode $file;
    return unless $handler; # exit if problems occur during handle creation
    #and stuff into the image handler.
    my $image = Wx::Image->new();
    my $bmp;    # used to hold the bitmap.
    $handler->LoadFile( $image, $file ) or print "can't load file!";
    $bmp = Wx::Bitmap->new($image);

    if( $bmp->Ok() ) {
        #  create a static bitmap called ImageViewer that displays the
        #  selected image.
        my $parent=$this->GetParent();
        my $img =$parent->{ImageViewer};
        my $size= $img->GetSize();
        $img->SetBitmap($bmp);
        if ($parent->{ScaleImage}){$img->SetSize($size)};
        # $parent->Clear();
        $img->Refresh;
    }
}

package main;

my $app = MyApp->new(); # create
$app->MainLoop();       # go