The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w
#############################################################################
## Name:        example/psexample.pl
## Purpose:     example for Wx::Perl::ProcessStream
## Author:      Mark Dootson
## Modified by:
## Created:     25/03/2007
## Copyright:   (c) 2007 Mark Dootson
## Licence:     This program is free software; you can redistribute it and/or
##              modify it under the same terms as Perl itself
#############################################################################
package ExecApp;
use Wx qw( :everything );
use base qw( Wx::App );

sub OnInit {
    my $self = shift;
    
    #----------------------------------------------------------------------
    # some non specific initialisation
    #----------------------------------------------------------------------

    Wx::InitAllImageHandlers;
    
    #----------------------------------------------------------------------
    # set application details
    #----------------------------------------------------------------------
    
    $self->SetAppName('My Exec Process Test Application');
    $self->SetVendorName('My Name');
    $self->SetClassName( $self->GetVendorName() . ' - ' . $self->GetAppName()  );
    
    #----------------------------------------------------------------------
    # create and show mainwindow
    #----------------------------------------------------------------------
    
    my $mwin = MainWindow->new(undef, -1); # parent = undef, ID = auto generated (-1)
    $self->SetTopWindow($mwin);
    $mwin->Centre;
    $mwin->Show(1);
    
    return 1;  
    
}

##########################

package main;

my $app = ExecApp->new();
$app->MainLoop;

########################## 

package MainWindow;
use Wx qw( :everything );
use Wx::Event qw( :everything );
use base qw( Wx::Frame );

use Wx::Perl::ProcessStream qw(
    EVT_WXP_PROCESS_STREAM_STDOUT
    EVT_WXP_PROCESS_STREAM_STDERR
    EVT_WXP_PROCESS_STREAM_EXIT
    wxpSIGKILL
    );

sub new {
    
    #----------------------------------------------------------------------
    # some defaults & SUPER constructor
    #----------------------------------------------------------------------
    
    $_[1] = undef                  if not exists $_[1];      # parent
    $_[2] = -1                     if not exists $_[2];      # id
    $_[3] = wxTheApp->GetAppName() if not exists $_[3];      # title
    $_[4] = wxDefaultPosition      if not exists $_[4];      # position
    $_[5] = wxDefaultSize          if not exists $_[5];      # size
    $_[6] = wxDEFAULT_FRAME_STYLE if not exists $_[6];      # style
   
    my $self = shift->SUPER::new(@_);
    
    $self->{menuindex} = {};
    $self->{menucount} = 0;
    $self->{controls} = {};
        
    #----------------------------------------------------------------------
    # menus with some keyboard shortcuts
    #----------------------------------------------------------------------
    
    my ($menu, $menuitem);
        
    # FILE MENU
    $menu = $self->add_menu('File', '&File');
    
    $menu->AppendSeparator();
    
    $menuitem = $self->add_menu_item('File', 'Exit', 'E&xit', \&evt_menu_file_exit);

    #----------------------------------------------------------------------
    # controls
    #----------------------------------------------------------------------
    
    # a panel as a parent for everything
    
    my $panelmain =  Wx::Panel->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL|wxNO_BORDER);
    # Wx::Panel->new(parent, id, position, size, flags);
     
    # add a TextCtrl to enter command line plus a label
    my $commandlbl = Wx::StaticText->new($panelmain, -1, 'Command Line', wxDefaultPosition, wxDefaultSize );
    my $commandtext = Wx::TextCtrl->new($panelmain, -1, '', wxDefaultPosition, wxDefaultSize );
    my $pointsize = $commandtext->GetFont->GetPointSize();
    $commandtext->SetFont(Wx::Font->new($pointsize, wxMODERN, wxNORMAL, wxNORMAL )); # fixed pitch
        
    # add an 'Execute' button
    my $execbutton = Wx::Button->new($panelmain, -1, 'Execute', wxDefaultPosition, wxDefaultSize );
    
    # add a TextCtrl to display results
    my $resulttext = Wx::TextCtrl->new($panelmain, -1, '', wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxTE_DONTWRAP);
    $pointsize = $resulttext->GetFont->GetPointSize();
    $resulttext->SetFont(Wx::Font->new($pointsize, wxMODERN, wxNORMAL, wxNORMAL )); # fixed pitch
    
    # store refs to the controls
    $self->add_control('PnlMain', $panelmain);
    $self->add_control('LblCommand', $commandlbl);
    $self->add_control('TxtCommand', $commandtext);
    $self->add_control('BtnExecute', $execbutton);
    $self->add_control('TxtResults', $resulttext);
    
    #------------------------------------------------------------------
    # Events
    #------------------------------------------------------------------
    
    EVT_BUTTON( $self, $self->get_control('BtnExecute'), \&evt_button_execute );
    EVT_WXP_PROCESS_STREAM_STDOUT( $self, \&evt_process_stdout);
    EVT_WXP_PROCESS_STREAM_STDERR( $self, \&evt_process_stderr);
    EVT_WXP_PROCESS_STREAM_EXIT( $self, \&evt_process_exit);
    
    Wx::Perl::ProcessStream->SetDefaultAppCloseAction( wxpSIGKILL );
    
    #----------------------------------------------------------------------
    # layout
    #----------------------------------------------------------------------
    
    my ($fillproportion, $flags, $bordersize);
    
    # create a main sizer for the frame
    $self->SetSizer( Wx::BoxSizer->new(wxVERTICAL) );
    
    $fillproportion = 1;
    $bordersize = 0;
    $flags = wxALL|wxEXPAND;
    
    $self->GetSizer->Add($panelmain, $fillproportion, $flags, $bordersize);
    
    # create a main sizer for the panel
    
    $panelmain->SetSizer( Wx::BoxSizer->new(wxVERTICAL) );
    
    # StaticBoxSizer for result text (framed box sizer with title)
    my $sizer_results = Wx::StaticBoxSizer->new(Wx::StaticBox->new($panelmain,-1,'Command Results'),wxVERTICAL);
    
    # FlexGridSizer for command text and label
    my $sizer_command = Wx::FlexGridSizer->new(0,2,0,10); # (rows, cols, vertical-spacing, horizontal-spacing)
                                                          # zero rows indicates grow dynamically
    $sizer_command->AddGrowableCol(1,1); # (column, proportion) - column index is zero based
    
    # BoxSizer for command buttons
    my $sizer_buttons = Wx::BoxSizer->new(wxHORIZONTAL);
    
        
    $fillproportion = 1;
    $bordersize = 3;
    $flags = wxALL|wxEXPAND;
    
    $sizer_command->Add($commandlbl, $fillproportion, $flags, $bordersize);
    $sizer_command->Add($commandtext, $fillproportion, $flags, $bordersize);
    $sizer_buttons->Add($execbutton, $fillproportion, $flags, $bordersize);
    $sizer_results->Add($resulttext, $fillproportion, $flags, $bordersize);
    
    $bordersize = 5;
    $panelmain->GetSizer->Add($sizer_command, 0, $flags, $bordersize);   
    
    $flags = wxALL|wxALIGN_RIGHT;
    $panelmain->GetSizer->Add($sizer_buttons, 0, $flags, $bordersize);
    
    $flags = wxALL|wxEXPAND;
    $panelmain->GetSizer->Add($sizer_results, $fillproportion, $flags, $bordersize);
    
    # layout the controls
    
    $self->SetAutoLayout(1);
    $self->Layout;
    
    # reference the panel using our method instead of scalar ref
    $self->get_control('PnlMain')->SetAutoLayout(1);
    $self->get_control('PnlMain')->Layout;
        
    # set a minimum size
    $self->SetSizeHints(500,400);
    
    # set default button
    $self->get_control('BtnExecute')->SetDefault();    
    
    # -- constructor complete
    return $self;
    
}


#----------------------------------------------------------------------
# Event Handlers
#----------------------------------------------------------------------

sub evt_menu_file_exit {
    my ($self, $event) = @_;
    $event->Skip(1);    # allow event to be processed by further handlers
    $self->Close;                    
}

sub evt_button_execute {
    my ($self, $event) = @_;
    $event->Skip(1);    # allow event to be processed by further handlers
    my $cmd = $self->get_control('TxtCommand')->GetValue();
    my $process = Wx::Perl::ProcessStream::Process->new($cmd, 'Perl Version', $self)->Run;
}

sub evt_process_stdout {
    my ($self, $event) = @_;
    $event->Skip(1);    # allow event to be processed by further handlers
    my $procname = $event->GetProcess->GetProcessName();
    my $line = $event->GetLine;
    my $apptext = '';
    $apptext .= qq(STDOUT: $procname: $line\n);
    $self->get_control('TxtResults')->AppendText($apptext);
}

sub evt_process_stderr {
    my ($self, $event) = @_;
    $event->Skip(1);    # allow event to be processed by further handlers
    my $procname = $event->GetProcess->GetProcessName();
    my $line = $event->GetLine;
    my $apptext = '';
    $apptext .= qq(STDERR: $procname: $line\n);
    $self->get_control('TxtResults')->AppendText($apptext);    
}

sub evt_process_exit {
    my ($self, $event) = @_;
    $event->Skip(1);    # allow event to be processed by further handlers
    
    my $exitcode = $event->GetProcess->GetExitCode();
    my $procname = $event->GetProcess->GetProcessName();
    $event->GetProcess->Destroy;
    my $apptext = qq(EXIT: $procname: $exitcode\n);
    $self->get_control('TxtResults')->AppendText($apptext);
}
    

#----------------------------------------------------------------------
# some crufty menu subs that save access to menus and controls for me
#----------------------------------------------------------------------

sub add_menu_item {
    my $self = shift;
    my ($menuname, $itemname, $itemstring, $coderef) = @_;
    $self->{menuitems}->{$menuname}->{$itemname} = Wx::MenuItem->new($self->{menus}->{$menuname}, -1, $itemstring, '', 0);
    $self->{menus}->{$menuname}->AppendItem($self->{menuitems}->{$menuname}->{$itemname});
    EVT_MENU($self, $self->{menuitems}->{$menuname}->{$itemname}->GetId(), $coderef);
    return $self->{menuitems}->{$menuname}->{$itemname};
}

sub add_menu {
    my $self = shift;
    my($menuname, $menustring) = @_;
    my $setmenubar = 0;
    if(!defined($self->{menubar})) {
        $self->{menubar}= Wx::MenuBar->new;
        $setmenubar = 1;
    }
    $self->{menus}->{$menuname} = Wx::Menu->new;
    $self->{menubar}->Append($self->{menus}->{$menuname},$menustring);  
    if($setmenubar) { $self->SetMenuBar($self->{menubar}); }
    $self->{menuindex}->{$menuname} = $self->{menucount};
    $self->{menucount} ++;
    return $self->{menus}->{$menuname};
}

sub add_submenu {
    my $self = shift;
    my($menuname, $submenuname, $submenustring) = @_;
    $self->{menus}->{$submenuname} = Wx::Menu->new;
    $self->{menus}->{$menuname}->AppendSubMenu($self->{menus}->{$submenuname}, $submenustring );
    $self->{menuindex}->{$menuname} = $self->{menucount};
    $self->{menucount} ++;
    return $self->{menus}->{$menuname};
}

sub add_control {
    my $self = shift;
    my ($controlname, $control) = @_;
    $self->{controls}->{$controlname} = $control;
}

sub get_control {
    my($self, $controlname) = @_;
    $self->{controls}->{$controlname};
}

1;