How to undersand the POE-Tk use of destroy?

2019-07-13 13:46发布

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"

标签: perl tk poe
1条回答
啃猪蹄的小仙女
2楼-- · 2019-07-13 14:24

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.

查看更多
登录 后发表回答