logo       
Google Custom Search
    AddThis Social Bookmark Button

Re: POE::Loop::Wx and minimalpoe.pl: msg#00021

Subject: Re: POE::Loop::Wx and minimalpoe.pl
I did some more work on this and now have a fairly useful functional minimalpoe.pl example.

1) You need POE installed.
2) You need the POE::Loop::Wx module in your path somewhere (attached)

This seems to run on both linux and win32 -- haven't tried under OS X yet.

To save space, I stripped out the commenting on the regular wx minimal stuff, but tried to add more comments explaining what I'm doing with POE.

Basically, each Wx frame runs as it's own POE session, created with the POEFrameWrapper->spawn statement. At this point, I still need a dummy POE session to keep POE alive, but I suspect having a "background" session that goes and polls for new data etc is not a bad thing.

POE::Kernel->run(); is used to start things instead of the $app->MainLoop();

Most of the work is done in the POEFrameWrapper class -- it creates the frame and puts it inside a new POE Session. I also was able to get a basic event build_handler working (got the ideas from POCO:SubWrapper component).

The cool thing about this event handler concept is that you can now let either the background session or another frame post events to a target frame. Ideas I plan to use this for: a) when submitting a change in one frame, force another frame to refresh, b) have the background session periodically poll a server for fresh data and then force a frame to refresh with the new data, etc.

Perhaps there are better Wx ways of doing this, but I like the flexibility that POE gives on this...

Mattia - would you be open to the POE::Loop::Wx module you did posted to the poe.perl.org site?




Mike Schroeder wrote:

Nothing too fancy yet - but at least it works! ;^)

Rename the POE-Loop-Wx.pm file to Wx.pm and put in path for POE/Loop/Wx.pm.

My big question so far is if it is appropriate to continue coding as we have
been in Wx, and code in normal POE style for that side of things?  That way
lets the two styles run in parallel under the same Loop.  Or, should we
adapt our style to make the Wx calls be more POE "states"?  For example,
should an EVENT just call a subref that does a POE yield? Or should we let
Wx continue to manage all of those things.

More questions than answers so far, but hopefully this is helpful to you.

Mike Schroeder


-----Original Message-----
From: wxperl-users-admin@xxxxxxxxxxxxxxxxxxxxx
[mailto:wxperl-users-admin@xxxxxxxxxxxxxxxxxxxxx]On Behalf Of Simon
Flack
On Wed, 18 Aug 2004 07:27:06 -0600, Mike Schroeder wrote
On a slightly different topic, I found a post from Mattia of an initial
version of POE::Loop::Wx for those wanting to combine POE with
wxPerl.  You could then use POE for many other non-blocking
interactions (poe.perl.org) So far I have invested a bit of time and
have the minimal.pl working with POE::Loop::Wx.  I still need to do
some more complex examples, as well as some thinking about how POE
affects programming style in wxPerl.  If anyone else some ideas or
examples, that would be great.  I'd be happy to share anything I
have so far.
Yes please. Funny, I was thinking about this a few weeks ago. I'd
like to see
some examples, even simple ones.
#!/usr/bin/perl
#############################################################################
## Name:        minimalpoe.pl
## Purpose:     Minimal wxPerl using POE::Loop::Wx sample
## Author:      Mike Schroeder based on minimal.pl from Mattia Barbon
## Modified by:
## Created:     11/10/2004
## Licence:     This program is free software; you can redistribute it and/or
##              modify it under the same terms as Perl itself
#############################################################################

use Wx;
use POE;

#-----------------------------------------------------------------------------
package MyApp;
#-----------------------------------------------------------------------------
use strict;
use base qw( Wx::App );

sub OnInit {
   my $self = shift;

   # We need to make sure the frame is part of self so that is available
   # later when we want to post events to the frame from other frames
   $self->{frame} = MyFrame->new(  "Minimal wxPerl and POE Example",
                                             Wx::Point->new( 50, 50 ),
                                             Wx::Size->new( 450, 350 )
                           );
   $self->SetTopWindow( $self->{frame} );
   $self->{frame}->Show( 1 );
   return 1;
}
1;

#-----------------------------------------------------------------------------
package MyFrame;
#-----------------------------------------------------------------------------
use strict;
use vars qw(@ISA);
@ISA=qw(Wx::Frame);
use Wx::Event qw(EVT_MENU);
use Wx qw(wxBITMAP_TYPE_ICO wxMENU_TEAROFF);

sub new {
  my( $class ) = shift;
  my( $this ) = $class->SUPER::new( undef, -1, $_[0], $_[1], $_[2] );
  $this->SetIcon( Wx::GetWxPerlIcon() );
  my( $mfile ) = Wx::Menu->new( undef, wxMENU_TEAROFF );
  my( $mhelp ) = Wx::Menu->new();
  my( $ID_ABOUT, $ID_EXIT ) = ( 1, 2 );
  $mhelp->Append( $ID_ABOUT, "&About...\tCtrl-A", "Show about dialog" );
  $mfile->Append( $ID_EXIT, "E&xit\tAlt-X", "Quit this program" );
  my( $mbar ) = Wx::MenuBar->new();
  $mbar->Append( $mfile, "&File" );
  $mbar->Append( $mhelp, "&Help" );
  $this->SetMenuBar( $mbar );
  EVT_MENU( $this, $ID_EXIT, \&OnQuit );
  EVT_MENU( $this, $ID_ABOUT, \&OnAbout );
  $this->CreateStatusBar( 1 );
  $this->SetStatusText( "Welcome to wxPerl!", 1 );
  $this;
}

sub OnCreateStatusBar {
  my( $this ) = shift;
  my( $status ) = Wx::StatusBar->new( $this, -1 );
  $status->SetFieldsCount( 2 );
  $status;
}

sub OnQuit {
  my( $this, $event ) = @_;
  $this->Close( 1 );
  Wx::wxTheApp->ExitMainLoop();
}

use Wx qw(wxOK wxICON_INFORMATION wxVERSION_STRING);
sub OnAbout {
  my( $this, $event ) = @_;
  Wx::MessageBox( "This is the about dialog of minimal sample.\n" .
                  "Welcome to wxPerl " . $Wx::VERSION . "\n" .
                  wxVERSION_STRING,
                  "About minimal", wxOK | wxICON_INFORMATION,
                  $this );
}

sub ext_event {
   my( $this, $event ) = @_;
   print "ext_event this is $this\n";
   # let one POE-managed Frame publish events to another POE-managed frame 
   # kindof like a Publish/Subscribe system between Wx frames
   # this could be used for auto-refresh of data from a background POE
   # session or letting one frame update the data on another frame, etc.
   print "got an ext_event\n";
  Wx::MessageBox( "This is an external event\n",
                  "External event", wxOK | wxICON_INFORMATION,
                  $this );
}

1;

#-----------------------------------------------------------------------------
package POEFrameWrapper;
#-----------------------------------------------------------------------------
use POE;
use Wx::Perl::Carp;

sub spawn {
   print "POEFrameWrapper->spawn: Entering\n";
   my $type = shift;
   my $package = shift; # the module/frame to wrap
   my $alias = shift;   # the POE Session alias to use
   my $evt_ref = shift; # the frame event routines to wrap as POE events
   croak "Too many args" if scalar @_;
   $alias = $package unless defined($alias) and length($alias);

   my %states;
   $states{'_start'} = sub {
      my ($kernel, $heap, $alias ) = @_[KERNEL, HEAP, ARG0 ];
      #$heap->{self} = eval("use $package; $package->new();");
      $heap->{self} = eval("$package->new();");
      if ( $@ ) {
         print "eval err: $@\n";
      }
      print "self is $heap->{self}\n";
      $kernel->alias_set($alias);
   };
   $states{'_stop'} = sub {
      my ($kernel, $heap) = @_[KERNEL, HEAP];
   };
   
   foreach my $event ( @{ $evt_ref } ) {
      print "building event handler for $event\n";
      $states{$event} = build_handler( $package, $event );
   }

   POE::Session->create(
          inline_states => \%states,
          args => [ $alias ],
                      );
  undef;
}

sub build_handler {
   my ( $pacakge, $sub ) = @_;
   my $ref = sub {
      my ($kernel, $heap, $sender, $callback, @args )
         = @_[KERNEL, HEAP, SENDER, ARG0, ARG1 .. $#_ ];
      my $self = $heap->{self};
      print "handler self = $self->{frame}\n";
      my $result = scalar $self->{frame}->$sub( @args );
      $kernel->post($sender, $callback, $result) if $callback;
      return;
   };
   return $ref;
}

1;

#-----------------------------------------------------------------------------
package main;
#-----------------------------------------------------------------------------
use POE;

# spawn a frame that will run as a POE session
POEFrameWrapper->spawn( 'MyApp', 'MyApp', [ qw( ext_event ) ] );

# for now, we need to start a background Session that keeps POE alive
# this could be some background process that checks for new data, etc.
# TODO - figure out how to get an open Wx frame to be enough reason for
# POE to stay alive...
POE::Session->create(
    inline_states => {
        _start => \&session_start,
        _stop  => \&session_stop,
        count  => \&session_count,
      }
);
POE::Kernel->run();

sub session_start {
    print "Session ", $_[SESSION]->ID, " has started.\n";
    $_[HEAP]->{count} = 0;
    $_[KERNEL]->delay_set("count",3);
}

sub session_stop {
    print "Session ", $_[SESSION]->ID, " has stopped.\n";
}

sub session_count {
    my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];
    my $session_id = $_[SESSION]->ID;
    my $count = ++$heap->{count};
    print "Session $session_id has counted to $count.\n";
    unless ( $count % 5 ) { # trigger an event every 5th cycle
      print "attempting to post to MyApp ext_event\n";
      $kernel->post('MyApp', 'ext_event', $count );
    }
    $kernel->delay_set("count", 3);
}
#############################################################################
## Name:        Wx.pm
## Purpose:     POE::Loop::Wx, wxPerl event loop for POE
## Author:      Mattia Barbon, 
## Created:     26/05/2003
## RCS-ID:      $Id: $
## Copyright:   (c) 2003 Mattia Barbon
## Note:        Part of the code comes almost straight from
##              POE::Loop::Gtk and POE::Loop::Select
## Licence:     This program is free software; you can redistribute it and/or
##              modify it under the same terms as Perl itself
#############################################################################

package POE::Loop::Wx;

use Wx;
use strict;
use vars qw($VERSION); 

$VERSION = "0.01";

package POE::Kernel;

use strict;

# Declare which event loop bridge is being used, but first ensure that
# no other bridge has been loaded.

BEGIN {
  die( "POE can't use Wx and " . &POE_LOOP_NAME . "\n" )
    if defined &POE_LOOP;
};

sub POE_LOOP () { LOOP_WX }

my @loop_vectors = ("", "", "");
my %loop_filenos;
my $time_watcher_timer;
my $file_watcher_timer;

###############################################################################
# Administrative functions
###############################################################################

sub loop_initialize {
    my $self = shift;

    # Initialize the vectors as vectors.
    @loop_vectors = ( "", "", "" );
    vec($loop_vectors[MODE_RD], 0, 1) = 0;
    vec($loop_vectors[MODE_WR], 0, 1) = 0;
    vec($loop_vectors[MODE_EX], 0, 1) = 0;

    # start file polling timer
    $file_watcher_timer = POE::Loop::Wx::PollTimer->new;
    $file_watcher_timer->Start( 50, 0 );
}

sub loop_finalize {
    my $self = shift;

    # This is "clever" in that it relies on each symbol on the left to
    # be stringified by the => operator.
    my %kernel_modes =
      ( MODE_RD => MODE_RD,
        MODE_WR => MODE_WR,
        MODE_EX => MODE_EX,
      );

    while (my ($mode_name, $mode_offset) = each(%kernel_modes)) {
        my $bits = unpack("b*", $loop_vectors[$mode_offset]);
        if (index($bits, "1") >= 0) {
            warn "<rc> LOOP VECTOR LEAK: $mode_name = $bits\a\n";
        }
    }

    $time_watcher_timer->Destroy;
    $file_watcher_timer->Destroy;
    undef $time_watcher_timer;
    undef $file_watcher_timer;
}

sub loop_do_timeslice {
    die "doing timeslices currently not supported in the Wx loop";
}

sub loop_run {
    Wx::wxTheApp()->MainLoop;
}

sub loop_halt {
    Wx::wxTheApp->ExitMainLoop;
}

###############################################################################
# Signal handling, straight from POE::Loop::Gtk
###############################################################################

#------------------------------------------------------------------------------
# Signal handlers/callbacks.

sub _loop_signal_handler_generic {
  if (TRACE_SIGNALS) {
    warn "<sg> Enqueuing generic SIG$_[0] event";
  }

  $poe_kernel->_data_ev_enqueue
    ( $poe_kernel, $poe_kernel, EN_SIGNAL, ET_SIGNAL, [ $_[0] ],
      __FILE__, __LINE__, time(),
    );
  $SIG{$_[0]} = \&_loop_signal_handler_generic;
}

sub _loop_signal_handler_pipe {
  if (TRACE_SIGNALS) {
    warn "<sg> Enqueuing PIPE-like SIG$_[0] event";
  }

  $poe_kernel->_data_ev_enqueue
    ( $poe_kernel, $poe_kernel, EN_SIGNAL, ET_SIGNAL, [ $_[0] ],
      __FILE__, __LINE__, time(),
    );
    $SIG{$_[0]} = \&_loop_signal_handler_pipe;
}

# Special handler.  Stop watching for children; instead, start a loop
# that polls for them.
sub _loop_signal_handler_child {
  if (TRACE_SIGNALS) {
    warn "<sg> Enqueuing CHLD-like SIG$_[0] event";
  }

  $SIG{$_[0]} = "DEFAULT";
  $poe_kernel->_data_ev_enqueue
    ( $poe_kernel, $poe_kernel, EN_SCPOLL, ET_SCPOLL, [ ],
      __FILE__, __LINE__, time(),
    );
}

#------------------------------------------------------------------------------
# Signal handler maintenance functions.

sub loop_watch_signal {
  my ($self, $signal) = @_;

  # Child process has stopped.
  if ($signal eq "CHLD" or $signal eq "CLD") {

    # For SIGCHLD triggered polling loop.
    # $SIG{$signal} = \&_loop_signal_handler_child;

    # Begin constant polling loop.  Only start it on CHLD or on CLD if
    # CHLD doesn"t exist.
    $SIG{$signal} = "DEFAULT";
    $self->_data_ev_enqueue
      ( $self, $self, EN_SCPOLL, ET_SCPOLL, [ ],
        __FILE__, __LINE__, time() + 1,
      ) if $signal eq "CHLD" or not exists $SIG{CHLD};

    return;
  }

  # Broken pipe.
  if ($signal eq "PIPE") {
    $SIG{$signal} = \&_loop_signal_handler_pipe;
    return;
  }

  # Artur Bergman (sky) noticed that xterm resizing can generate a LOT
  # of WINCH signals.  That rapidly crashes perl, which, with the help
  # of most libc"s, can"t handle signals well at all.  We ignore
  # WINCH, therefore.
  return if $signal eq "WINCH";

  # Everything else.
  $SIG{$signal} = \&_loop_signal_handler_generic;
}

sub loop_ignore_signal {
  my ($self, $signal) = @_;
  $SIG{$signal} = "DEFAULT";
}

# End of stuff copied from POE::Loop::Gtk

sub loop_attach_uidestroy {
    my( $self, $window ) = @_;

    # Don"t bother posting the signal if there are no sessions left.  I
    # think this is a bit of a kludge: the situation where a window
    # lasts longer than POE::Kernel should never occur.
    Wx::Event::EVT_CLOSE( $window,
                          sub {
                              if( $self->_data_ses_count() ) {
                                  $self->_dispatch_event
                                    ( $self, $self,
                                      EN_SIGNAL, ET_SIGNAL, [ "UIDESTROY" ],
                                      __FILE__, __LINE__, time(), -__LINE__
                                    );
                              }
                              return undef;
                          }
                        );
}

###############################################################################
# Alarm or timer functions
###############################################################################

sub loop_reset_time_watcher {
    my( $self, $next_time ) = @_;

    if( $time_watcher_timer ) {
        $time_watcher_timer->Destroy;
        undef $time_watcher_timer;
    }

    $time_watcher_timer = POE::Loop::Wx::Timer->new;
    $self->loop_resume_time_watcher( $next_time );
}

BEGIN {
    if( $^O eq "MSWin32" ) {
        eval "sub MIN_TIME() { 1 }";
    } else {
        eval "sub MIN_TIME() { 0 }";
    }
}

sub loop_resume_time_watcher {
    my( $self, $next_time ) = @_;

    $time_watcher_timer = POE::Loop::Wx::Timer->new
        unless $time_watcher_timer;

    $next_time -= time();
    $next_time *= 1000;
    $next_time = MIN_TIME if $next_time <= 0;

    $time_watcher_timer->Start( $next_time, 1 );
}

sub loop_pause_time_watcher {
    $time_watcher_timer->Stop;
}

###############################################################################
# File activity functions; similar to POE::Loop::Select
###############################################################################

#------------------------------------------------------------------------------
# Maintain filehandle watchers.

sub loop_watch_filehandle {
    my( $self, $handle, $mode ) = @_;
    my $fileno = fileno( $handle );

    vec( $loop_vectors[$mode], $fileno, 1 ) = 1;
    $loop_filenos{$fileno} |= ( 1 << $mode );
}

sub loop_ignore_filehandle {
    my( $self, $handle, $mode ) = @_;
    my $fileno = fileno( $handle );

    vec( $loop_vectors[$mode], $fileno, 1 ) = 0;
    $loop_filenos{$fileno} &= ~ ( 1 << $mode );
}

sub loop_pause_filehandle {
    my( $self, $handle, $mode ) = @_;
    my $fileno = fileno( $handle );

    vec( $loop_vectors[$mode], $fileno, 1 ) = 0;
    $loop_filenos{$fileno} &= ~ ( 1 << $mode );
}

sub loop_resume_filehandle {
    my( $self, $handle, $mode ) = @_;
    my $fileno = fileno( $handle );

    vec( $loop_vectors[$mode], $fileno, 1 ) = 1;
    $loop_filenos{$fileno} |= ( 1 << $mode );
}

# End of stuff similar to POE::Loop::Select

package POE::Loop::Wx::Timer;

use strict;
use base "Wx::Timer";

sub Notify {
    package POE::Kernel;

    my $self = $poe_kernel;

    $self->_data_ev_dispatch_due();
    $self->_test_if_kernel_is_idle();

    # Register the next timeout if there are events left.
    if( $self->get_event_count() ) {
        $self->loop_resume_time_watcher( $self->get_next_event_time() );
    }
}

package POE::Loop::Wx::PollTimer;

use strict;
use base "Wx::Timer";

sub Notify {
    package POE::Kernel;

    my $self = $poe_kernel;

    # Determine which files are being watched.
    my @filenos = ();
    while( my( $fd, $mask ) = each( %loop_filenos ) ) {
        push( @filenos, $fd ) if $mask;
    }

    return unless @filenos;

    # Check filehandles, or wait for a period of time to elapse.
    my $hits = select( my $rout = $loop_vectors[MODE_RD],
                       my $wout = $loop_vectors[MODE_WR],
                       my $eout = $loop_vectors[MODE_EX],
                       0,
                     );

    return unless $hits > 0;

    # This is where they"re gathered.  It"s a variant on a neat
    # hack Silmaril came up with.
    my( @rd_selects, @wr_selects, @ex_selects );
    foreach ( @filenos ) {
        push( @rd_selects, $_ ) if vec( $rout, $_, 1 );
        push( @wr_selects, $_ ) if vec( $wout, $_, 1 );
        push( @ex_selects, $_ ) if vec( $eout, $_, 1 );
    }

    @rd_selects and
      $self->_data_handle_enqueue_ready( MODE_RD, @rd_selects );
    @wr_selects and
      $self->_data_handle_enqueue_ready( MODE_WR, @wr_selects );
    @ex_selects and
      $self->_data_handle_enqueue_ready( MODE_EX, @ex_selects );
}

1;

__END__

Try Searching:
servers, voip, java, networking, microsoft ...
<Prev in Thread] Current Thread [Next in Thread>