Click here to get back home

Perl Tk:Scheduler command called subroutine fails to talk to tk widgets

 HomeNewsGroups | Search | About
 comp.lang.perl.modules    Post an article   get this group's latest topics as an RSS feed add this group's latest topics to your My MSN content add this group's latest topics to your My Yahoo content
Subject Author Date
Perl Tk:Scheduler command called subroutine fails to talk to tk widgets Andy 01-31-2007
Get Chitika Premium
Posted by Andy on February 5, 2007, 2:32 pm
Please log in for more thread options


Thanks Dominique!

I'ld like to recap/summarize this discussion thread to make it easier
for those reading it in the future.

Closure is the Perl equivalent of namespaces and encapsulation of
variables and code. Since Perl programs are executed by a central
interpreter, there is no O/S imposed isolation of scope between Perl
programs and the modules they call (they all run under the same
process the interpreter is on).

As long as you pass a reference (a memory address) for a variable or a
subroutine, that variable can be read/written or that subroutine can
be executed from anywhere, regardless of where it was first declared.
Reference passing through switches (such as -command) is a common way
for Perl/Tk programs and widgets to talk to each other.

To pass a reference for a subroutine declared in a Perl program to a
widget, prefix the subroutine's name with \&

To pass a reference for a variable declared in a Perl program to a
widget, prefix the variable's name with $

To execute an externally referenced subroutine from inside a Perl
module, precede the variable that received the reference with &$ and
treat the entire thing as a normal subroutine call.

To work with the value in an externally referenced variable from
inside a Perl module, precede the variable that received the reference
with $$ and treat the entire thing as a normal variable.

ie, given myvar and mysub reside in your perl program

$myvar="hello world";

mysub{
($greeting) = @_;
print $$greeting;
}

you can have a perl module (or widget) directly access these items
without making any copies of anything by first passing the references:

-command => [\&mysub,$myvar]

and having the receiving perl module (or widget) use the references to
execute the external code in some way:

modsub=shift; #captures the ref \&mysub
modvar=shift; #captures the ref $myvar

&$modsub($$modvar); #call the external mysub method and pass it hello
world


The &$ and $$ are called dereferencing operators, and they can also be
used anywhere to "execute" a reference wherever it appears.



There are, however, some Perl Modules that execute a copy of
referenced code (by value) rather than doing it directly (by
reference).

These use another CPAN module called B::Deparse that is essentially a
Perl de-compiler. Given a reference, Deparse can expand the tokens it
finds there into Perl code using its coderef2text function:

use B::Deparse;
$deparse= B::Deparse->new();
modsub=shift; #captures the ref \&mysub
$modcode=$deparse->coderef2text(modsub); #modcode now contains mysub
listing

The Perl module then re-compiles the de-compiled code using eval() to
execute it:

eval($modcode);


This is useful when your Perl program needs memory to be persisted in-
between separate invocations of the Perl interpreter. Rather than
persisting addresses (which can change upon reloading), it's easier to
recreate whatever needs persistance and to assign it a new memory
address.

The tk::scheduler does this because it persists scheduled tasks, even
when it is shut down and restarted again at a later date. If the
scheduler stored the addresses to scheduled code, those addresses
could be invalid the next time it was reloaded back into the
interpreter. Instead, it stores a copy of the subroutine to be
executed, which is why I encountered all the previous access problems
to subroutines other than my main one - they weren't included in the
deparser decompile.

The deparser, however, is smart enough to include any package names
for the listing the decompiled code originally appeared in.

The package name acts as a namespace that identifies a particular
symbolic table that may exist in the Perl Interpreter. When the
eval() statement has a package name, it can access the symbolic table
to resolve any other references within the decompiled code that point
to code currently loaded in the Perl interpreter. In a way, execute
by value code can execute code by reference by using labels instead of
numeric memory addresses.


Posted by Dominique Dumont on February 6, 2007, 3:34 am
Please log in for more thread options



> Thanks Dominique!

You're welcome.

> ie, given myvar and mysub reside in your perl program
>
> $myvar="hello world";
>
> mysub{
> ($greeting) = @_;

Beware, here, $greeting is a global variable. You should use a
lexical variable:

my ($greeting) = @_ ;

> print $$greeting;
> }

[snip]

> you can have a perl module (or widget) directly access these items
> without making any copies of anything by first passing the references:
>
> -command => [\&mysub,$myvar]
>
> and having the receiving perl module (or widget) use the references to
> execute the external code in some way:
>
> modsub=shift; #captures the ref \&mysub
> modvar=shift; #captures the ref $myvar

You forgot the $ (and probably the lexical declaration):

my $modsub=shift; #captures the ref \&mysub
my $modvar=shift; #captures the ref $myvar

> &$modsub($$modvar); #call the external mysub method and pass it hello
> world

This can also be run with this syntax:

$modsub -> ($$modvar) ;

> The &$ and $$ are called dereferencing operators, and they can also be
> used anywhere to "execute" a reference wherever it appears.
>
>
>
> There are, however, some Perl Modules that execute a copy of
> referenced code (by value) rather than doing it directly (by
> reference).
>
> These use another CPAN module called B::Deparse that is essentially a
> Perl de-compiler. Given a reference, Deparse can expand the tokens it
> finds there into Perl code using its coderef2text function:
>
> use B::Deparse;
> $deparse= B::Deparse->new();
> modsub=shift; #captures the ref \&mysub
> $modcode=$deparse->coderef2text(modsub); #modcode now contains mysub

You forgot the $ (and probably the lexical declaration):

use B::Deparse;
my $deparse = B::Deparse->new();
my $modsub = shift; #captures the ref \&mysub

# modcode now contains mysub listing
my $modcode = $deparse->coderef2text($modsub);


> The tk::scheduler does this because it persists scheduled tasks, even
> when it is shut down and restarted again at a later date. If the
> scheduler stored the addresses to scheduled code, those addresses
> could be invalid the next time it was reloaded back into the
> interpreter. Instead, it stores a copy of the subroutine to be
> executed, which is why I encountered all the previous access problems
> to subroutines other than my main one - they weren't included in the
> deparser decompile.

I hope there's a big warning in Tk::Scheduler doc. If not, I think you
should send a patch to its author.

Cheers

--
Dominique Dumont
"Delivering successful solutions requires giving people what they
need, not what they want." Kurt Bittner

Posted by Andy on February 6, 2007, 11:08 am
Please log in for more thread options


How a Perl Module is implemented determines what referencing
techniques you have to use to make it work. Here's another example
that might help those in the future that mixes radiobutton widgets,
text widgets, and the tk::scheduler.

Unlike text widgets, radiobutton state cannot be read directly.
Instead, radiobuttons dynamically set (by reference) a shared
variable. In this example, this variable has to also be accessed by
the scheduler which does everything by value. I've created my own
radiobutton state variable called $environment. The text box address
is stored in $results.

package Tk::BulkE;

use Tk;
use Tk::ROText;
use Tk::Schedule;

my $environment=0;

#CREATE AN APPLICATION WINDOW (SEE PERL/TK LAYOUT MANAGERS)
my $mw = MainWindow->new;
my $frame8 = $mw->Frame; # scheduler
$frame8->pack(-fill => "x");
my $frame4 = $mw->Frame;
$frame4->pack(-fill => "x",
-pady => 5);
my $frame5 = $mw->Frame;
$frame5->pack(-fill => "x",
-pady => 5);


#THIS IS A TEXTBOX TO DISPLAY MESSAGES IN
my $results=$frame4->Text(-width => 79,
-height => 8,
-background => "#7800e400ff00"
)->pack();


#TK::RADIOBUTTONS TO SELECT A STATE (BY REF CALLING)
my $optDev=$frame5->Radiobutton(-text => "Development",
-value => "dev", # NOT REALLY USING THIS
-command => sub{
package Tk::BulkE; #DEPARSE NOT USED HERE, SO ADD THE PACKAGE
NAME
$results->configure(-background=> "#7800e400ff00");
$results->insert(end, "WARNING: Email will be sent internally
\n");
$results->see(end);
setEnvironment(0);
}
)->pack(-side => "left");
my $optProd=$frame5->Radiobutton(-text => "Production",
-variable => $mode,
-value => "prod", # NOT REALLY USING THIS
-command => sub{
package Tk::BulkE; #DEPARSE NOT USED HERE, SO ADD THE PACKAGE
NAME
$results->configure(-background=> "#ff00d9000000");
$results->insert(end, "CAUTION: Email will be sent to outside
\n");
$results->see(end);
setEnvironment(1);
}
)->pack(-side => "left");

#PASS REFERENCES TO THE SCHEDULER (BY VALUE CALLING)
my $ctrl={};
$ctrl=$results; #ADDRESS FOR TEXT WIDGET
$ctrl=$environment; #REFERENCE TO RADIOBUTTON STATE VARIABLE
my $s = $frame8->Schedule(
-interval => 60,
-repeat => "once",
-command => [\&run, #PASSED BY REFERENCE
$ctrl #BY REFERENCE ADDRESSES PASSED BY VALUE
],
-comment => "1-9999"
)->pack(-fill => "x",
-padx => 25);

MainLoop;

#RADIOBUTTON COMMAND SWITCH CALLS THIS
sub setEnvironment{
$environment=shift;
anothersub($results);
}

#SCHEDULER COMMAND SWITCH CALLS THIS
sub run{
#DEPARSE AUTOMATICALLY ADDS package Tk::BulkE; HERE

my $ctrl=shift; #get the references

$ctrl->insert(end, "Task started\n"); #DISPLAY MESSAGE IN
TEXT WIDGET
my $mode=$ctrl; #CAPTURE THE ADDRESS FOR RADIOBUTTON STATE
VARIABLE

#ACT ON RADIOBUTTON STATE
if($$mode>0){
$ctrl->insert(end, "Production run requested\n");
anothersub($ctrl);
}else{
$ctrl->insert(end, "Development run requested\n");
}
}

#BOTH THE RADIOBUTTON AND SCHEDULER COMMAND SWITCHES CALL THIS
sub anothersub{
(my $results)= @_;
$results->insert(end, "this works too!\n");
}

That's it for this story - see'ya everyone!


Posted by Dominique Dumont on February 5, 2007, 5:46 am
Please log in for more thread options



> I've read your reply and tried moving the run subroutine into a
> variable, and declaring it and the tk read only text widget before the
> creation of the scheduler. The code compiles and runs. But, when the
> scheduler tries to parse the code in the command variable, the
> following error occurs (buried deep inside of some perl modules the
> scheduler itself uses):
>
> Tk::Error: Usage: ->coderef2text(CODEREF) at C:/Perl/site/lib/Tk/
> Schedule.pm line 374
> Carp::croak at C:/Perl/lib/Carp.pm line 269
> B::Deparse::coderef2text at C:/Perl/lib/B/Deparse.pm line 671
> Tk::Schedule::AddTime at C:/Perl/site/lib/Tk/Schedule.pm line 374

Looks like Schedule use deparse and then (probably) eval. In this
case, closure will not work as the lexical variables defined in your
module are not in the scope of the eval run by Schedule.

> Tk callback for .schedule.frame.button
> Tk::__ANON__ at C:/Perl/site/lib/Tk.pm line 252
> Tk::Button::butUp at C:/Perl/site/lib/Tk/Button.pm line 111
> <ButtonRelease-1>
> (command bound to event)
>
> As I haven't written the scheduler or the other modules it uses, I
> have no idea what this error is saying went wrong. But, I suspect it
> has to do with something with the command code being treated as plain
> text before it is executed in the eval statement within the
> shceduler. If so, we seem to be losing the memory references to
> variables again.

Yes, because lexical variable cannot get through a deparse/eval. OTOH,
regular variables can.

> But, thanks for the suggestion.

You're welcome.

Cheers

--
Dominique Dumont
"Delivering successful solutions requires giving people what they
need, not what they want." Kurt Bittner

Similar ThreadsPosted
comp.lang.perl.modules,alt.autos.rod-n-custom,alt.kids-talk,alt.internet.p2p,alt.support.nutty.as.a.fruitcake December 28, 2004, 2:24 am
use of xml in perl application to describe command line logic December 20, 2005, 11:03 am
Perl/Tk build fails on HP-UX February 27, 2006, 4:58 pm
Net::LDAP makefile generation fails on ActiveState Perl 5.8.8 June 23, 2006, 2:57 am
Log::Dispatch - How to "die" a script after all other methods are called??? September 6, 2005, 4:55 pm
Curses::Widgets::Menu Question May 11, 2005, 8:32 pm
Compile problem: Curses::Widgets September 24, 2007, 2:54 pm
SOLVED: Curses::Widgets Runtime Error October 13, 2007, 3:01 pm
"Undefined subroutine" November 21, 2004, 8:48 pm
catch_int\catch_hup subroutine February 25, 2005, 8:51 am

Our other projects:

Art Dolls, Fairies and Mermaids - Sunnyfaces.net

Roy's Linux, Programming and Search Engines messages

1-Script XML SitemapXML Sitemap