2

Here is some test code to illustrate my problem;


use Tk;
use POE qw( Loop::TkActiveState );
use Tk::Toplevel;

POE::Session->create(
    inline_states => {
        _start      => \&ui_start
        ,top1       => \&top1
        ,top2       => \&top2
#       ,kill_top1  => \&kill_top1
        ,kill_top1  =>  sub {
            $heap->{tl1}->destroy;
        }
        ,over       => sub { exit }
    }
);

$poe_kernel->run();
exit 0;

sub ui_start {
    my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
    $heap->{mw} = $poe_main_window;
    $but1 = $heap->{mw}->Button(
        -text => 'Exit',
        -width => 12,
        -command => $session->postback("over")
    )->pack( -padx => 7,
        -side => 'left',
        -expand => 0 );

    $but2 = $heap->{mw}->Button(
        -text => 'Top1',
        -width => 12,
        -command => $session->postback("top1")
    )->pack( -padx => 7,
        -side => 'left',
        -expand => 0 );
    $but2 = $heap->{mw}->Button(
        -text => 'Top2',
        -width => 12,
        -command => $session->postback("top2")
    )->pack( -padx => 7,
        -side => 'left',
        -expand => 0 );
    $but3 = $heap->{mw}->Button(
        -text => 'Kill TL',
        -width => 12,
        -command => $session->postback("kill_top1")
    )->pack( -padx => 7,
        -side => 'left',
        -expand => 0 );
}

sub top1 {
    my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
    unless(Tk::Exists($heap->{tl1})) {
        $heap->{tl1} = $heap->{mw}->Toplevel( title => "Top1");
    }
}   

sub top2 {
    my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
    $heap->{tl2} = $heap->{mw}->Toplevel( title => "Top2");
    $heap->{tl1}->destroy if Tk::Exists($heap->{tl1});
}   

sub kill_top1 {
    my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
    $heap->{tl1}->destroy if Tk::Exists($heap->{tl1});
}

If I uncomment the version of the inline state kill_top1, all is well. If I use the version (as shown) that calls the anonymous sub, I get;


C:\scripts\alias\resource>alias_poe_V-3_0_par.pl
 error:Can't call method "destroy" on an undefined value at C:\scripts\alias\res
ource\alias_poe_V-3_0_par.pl line 328,  line 365.

Tk::Error: Can't call method "destroy" on an undefined value at C:\scripts\alias
\resource\alias_poe_V-3_0_par.pl line 328,  line 365.
 Tk::After::once at C:/Perl/site/lib/Tk/After.pm line 89
 [once,[{},undef,100,once,[\&POE::Kernel::_poll_for_io]]]
 ("after" script)

In this posting [link text][1] Rocco Caputo explains;

"Tk is not passing the event information to POE.

As you know, postbacks are anonymous subroutine references that post POE events when they're called. They're used as a thin, flexible interface between POE and Tk, among other things.

Postbacks are blessed, and their DESTROY methods are used to notify POE when Tk is done with them. From Tk's point of view, the only difference between a callback and a postback is this blessing.

For some reason, Tk does not pass parameters to a blessed callback."

He gives a workaround, but I am not sure 1) if this is the issue I have uncovered or )2 if it is, how to apply the workaround.

[1]: http://osdir.com/ml/lang.perl.poe/2004-01/msg00002.html :Tk With POE - bind() function for keypresses"

jpolache
  • 305
  • 3
  • 12
  • 1
    when you do the first $heap->{tl1}->destroy, where do you get your $heap from? I can't see it defined anywhere. – Inshallah Jul 17 '09 at 21:50
  • What's wrong with putting the commas where they belong? They hurt my eyes. – Sinan Ünür Jul 17 '09 at 23:31
  • Of course there is nothing wrong with putting commas at the end of the line. I started putting them at the beginning after creating a syntax error when I did a cut-and-paste on a group of settings to a widget and wound up leaving a line without a comma in the middle of the stack. I apologize if this caused you any discomfort. – jpolache 0 secs ago [delete this comment] – jpolache Jul 20 '09 at 13:50
  • In Perl, you can add a comma after the last entry of a list. It's even recommended for the kind of list that you have there. – Inshallah Jul 20 '09 at 18:26

1 Answers1

3

It sure looks like you've hit the problem that Rocco is describing. Basically, your closure (the sub {...}) has access to $heap because $heap is in scope when you create the closure. On the other hand, when you use the &kill_top1 function reference it appears you're not getting any parameters passed in, which means @_[HEAP] is undefined.

Using the closure seems to work, but if you wanted to "fake" it, you could replace it with:

kill_top1 => sub { 
    @args[KERNEL,SESSION,HEAP] = ($kernel,$session,$heap);
    kill_top1(@args);
}

This would be my preference, just to keep the interface to, and event handling of, kill_top1 the same as all the others.

Andrew Barnett
  • 5,066
  • 1
  • 22
  • 25