The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Tk::AppWindow::CookBook - Some recipies

OTHER RECIPIES

Tk::AppWindow::CookBook::Extension
Tk::AppWindow::CookBook::Plugin
Tk::AppWindow::CookBook::ContentManager

A SIMPLE IMAGE VIEWER

#!/usr/bin/perl

use strict;
use warnings;


################################################################
# First define the content manager. a content manager handles  #
# the basic tasks like loading, saving, displaying and         #
# modifying files. The content manager does the actual work.   #
################################################################
package ImageManager;

use base qw(Tk::Derived Tk::AppWindow::BaseClasses::ContentManager);
Construct Tk::Widget 'ImageManager';
require Tk::Pane;
require Tk::Photo;
use Tk::PNG;
use File::Basename;
use MIME::Base64;
use Imager;

#Determining which image types we can use;
my %photoext = ();
my @types = keys %Imager::formats;
for (@types) {
   $photoext{'.jpg'} = 1 if $_ eq 'jpeg';
   $photoext{".$_"} = 1;
}

sub Populate {
   my ($self, $args) = @_;
   
   $self->SUPER::Populate($args);
   #We create a scrolled pane as main viewer. 
   #In it we pack a label with the image.
   my $pn = $self->Scrolled('Pane',
      -scrollbars => 'osoe',
   )->pack(-expand => 1, -fill => 'both');
   $self->CWidg($pn);
   $self->{FILE} = '';
   $self->{IMAGE} = undef;
   $self->ConfigSpecs(
      -zoomto => ['PASSIVE', undef, undef, 'fit'],
      DEFAULT => [$pn],
   );
}

#overwritten method
sub doClear {
   my $self = shift;
   $self->{FILE} = '';
   $self->ViewerClear;
   $self->{IMAGE} = undef;
}

#overwritten method
sub doLoad {
   my ($self, $file) = @_;
   $self->{FILE} = $file;
   if (-e $file) {
      my ($name,$path,$suffix) = fileparse(lc($file), keys %photoext);
      if (exists $photoext{lc($suffix)}) {
         my $img = Imager->new;
         $img->read(file=>$file) or warn "Cannot read: ", $img->errstr;
         $self->{IMAGE} = $img if defined $img;
      } else {
         warn "Not an image $file"
      }
   } else {
      warn "$file does not exist";
   }
   $self->after(100, ['ZoomDefault', $self]);
   return 1
}

sub ZoomDefault {
   my $self = shift;
   my $scale = $self->cget('-zoomto');
   if ($scale eq 'fit') {
      $self->ZoomToFit
   } else {
      $self->Zoom($scale)
   }
}

sub Zoom {
   my ($self, $perc) = @_;
   return $self->ZoomToFit if $perc eq 'fit';
   my $scale = $perc / 100;
   my $img = $self->{IMAGE};
   if (defined $img) {
      my $width = int($img->getwidth * $scale);
      my $height = int($img->getheight * $scale);
      my $new = $img->scale(xpixels => $width, ypixels => $height);
      $self->ViewerClear;
      my $data;
      $new->write(data => \$data, type => 'png');
      my $l = $self->CWidg->Label(
         -image => $self->Photo(
            -data => encode_base64($data), 
            -format => 'png',
         ),
      )->pack;
      $self->{LABEL} = $l;
   }
}

sub ZoomToFit {
   my $self = shift;
   my $img = $self->{IMAGE};
   if (defined $img) {
      my $pane = $self->CWidg;
      my $x = $pane->width - 4;
      my $y = $pane->height - 4;
      my $width = $img->getwidth;
      my $height = $img->getheight;
      my $scale;
      my $xs = $x / $width;
      my $ys = $y / $height;
      if ($xs > $ys) {
         $scale = $ys
      } else {
         $scale = $xs
      }
      $self->Zoom($scale * 100);
   }
}

sub ViewerClear {
   my $self = shift;
   my $i = $self->{LABEL};
   $i->destroy if defined $i;
   $self->{LABEL} = undef;
   $self->update;
}

################################################################
# Now that we have a content manager ...                       #
# We can define the main package                               #
################################################################

package main;

require Tk::AppWindow;

my @scales = (400, 200, 100, 50, 25, 10);

my $iv;
$iv = Tk::AppWindow->new(
   -appname => 'Siv',
   -contentmanagerclass => 'ImageManager',
   -contentmanageroptions => ['-scaleto'],
   -extensions => [qw[Art Balloon MDI ToolBar StatusBar MenuBar Navigator Settings]],
   -readonly => 1,
   -mainmenuitems => [
      [   'menu',             'View',         "~Zoom"    ], 
      [   'menu_normal',      'Zoom::',      "~Fit",         'scaletofit',            'transform-scale' ], 
      [   'menu_normal',      'Zoom::',      "400",         'scaleto400',            'transform-scale' ], 
      [   'menu_normal',      'Zoom::',      "200",         'scaleto200',            'transform-scale' ], 
      [   'menu_normal',      'Zoom::',      "100",         'scaleto100',            'transform-scale' ], 
      [   'menu_normal',      'Zoom::',      "50",         'scaleto50',            'transform-scale' ], 
      [   'menu_normal',      'Zoom::',      "25",         'scaleto25',            'transform-scale' ], 
      [   'menu_normal',      'Zoom::',      "10",         'scaleto10',            'transform-scale' ], 
   ],
   -useroptions => [
      '*page' => 'Bars',
       '*section' => 'Menubar',
       -menuiconsize => ['list', 'Icon size', -values => sub { return $iv->cmdExecute('available_icon_sizes') }],
       '*end',
      '*section' => 'Toolbar',
      -toolbarvisible => ['boolean', 'Visible at launch'],
       -tooliconsize => ['list', 'Icon size', -values => sub { return $iv->cmdExecute('available_icon_sizes') }],
      -tooltextposition => ['radio', 'Text position', -values => [qw[none left right top bottom]]],
      '*end',
      '*section' => 'Statusbar',
      -statusbarvisible => ['boolean', 'Visible at launch'],
      '*end',
   ],
);

$iv->cmdConfig('scaletofit' => [\&Zoom, 'fit']);
for (@scales) {
   my $scale = $_;
   $iv->cmdConfig("scaleto$scale" => [\&Zoom, $scale])
}

$iv->MainLoop;

sub Zoom {
   my $perc = shift;
   my $mdi = $iv->extGet('MDI');
   my $sel = $mdi->docSelected;
   if (defined $sel) {
      my $doc = $mdi->docGet($sel);
      $doc->Zoom($perc);
   }
}

AUTHOR

Hans Jeuken (hanje at cpan dot org)

SEE ALSO

Tk::AppWindow
Tk::AppWindow::BaseClasses::Extension
Tk::AppWindow::BaseClasses::SidePanel
Tk::AppWindow::Ext::Panels