skywalker has asked for the wisdom of the Perl Monks concerning the following question:

Hi, is there any way that I can bind an update on the Mainwindow on Tk with a scalar value, rather than a user interaction.

ie at the moment I can bind to an event (keyboard hit) with

$mw -> bind('<Button-1>' => sub {print "actioned"});
however I would like to be able to achive the same result with a scalar;
$mw -> bind($shared_scalar => sub {print "actioned"});
or can I initiate a pesudo key press to get the same result.

note: I have a GUI running a listbox with a shared array, which is processed by a thread, I need the GUI to update the display when the thread shifts a value from the array,

thanks
skywalker

Replies are listed 'Best First'.
Re: Tk Bind scalar
by zentara (Cardinal) on Feb 20, 2009 at 19:01 UTC
Re: Tk Bind scalar
by jdporter (Paladin) on Feb 20, 2009 at 19:37 UTC

    You can cheat by using a hidden (unmapped) widget which watches its bound variable. The likeliest victim would probably be a Tk::Entry.

    sub watch_variable { my( $mainwindow, $scalarref, $coderef ) = @_; $mainwindow->Entry( -textvariable => $scalarref, -validate => 'all', -validatecommand => sub { $coderef->( $_[0] ); 1 }, ); }

    Here's a little demo program:

    use Tk; use strict; use warnings; my $mw = new MainWindow; my $var; watch_variable( $mw, \$var, sub { warn "val = $_[0]\n" } ); $mw->Button( -text => "Incr", -command => sub { $var++ } )->pack; $mw->Button( -text => "Set 5", -command => sub { $var = 5 } )->pack; $mw->Button( -text => "Exit", -command => sub { exit } )->pack; MainLoop; sub watch_variable { my( $mainwindow, $scalarref, $coderef ) = @_; $mainwindow->Entry( -textvariable => $scalarref, -validate => 'all', -validatecommand => sub { $coderef->( $_[0] ); 1 }, ); }

    Note that the callback is only called when the value of the variable is changed, which is not necessarily every time it is assigned to.

    Also note that if the variable has a defined value at the time you call watch_variable (i.e., technically, at the time you tell the Entry widget to watch it), you will get a callback on it immediately, since it is detected as a change from the initial value, which is undef.

    I think both of these may be the desired behaviors... depending on your app.

    Between the mind which plans and the hands which build, there must be a mediator... and this mediator must be the heart.
      Both those methods will do what I require which is great, however as soon as I try to share the reference the value on the traceVariable does not update, and on the Watch_variable the compiler complains 'attempt to free non-existent shared string'.

      my $v = 0; share ($v); $mw->traceVariable(\$v, 'w' => [\&update_meter]); my $thr = threads->new(\&sub1); sub sub1{ while (1){ $v+= 1; sleep(1); print $v.":"; } }

      thanks skywalker

        Oh... Threads? I don't know. The conventional wisdom is, or at least used to be, that synchronous event-driven systems (e.g. GUIs like Tk) and asynchronous threading models don't really play well together, since the problem spaces they attempt to address are nearly the same, yet they take entirely different approaches. Maybe you could consider using POE, which (I've heard) integrates very nicely with Tk (see POE::Loop::Tk). That would get you out of the realm of threads, which is even more messy in Perl than in some other popular languages.

        Well at a glance, shared vars for threads, need to be scalars.....I don't think it can be an object reference...... so maybe that would be a worthwhile feature of threads in Perl6?

        I know now in Tk, I need to have a timer running to read shared vars in a thread. A thread dosn't affect main's globals automatically.. An interesting trick I didn't try yet was communicating back thru filneo's......the threads all share those fd numbers.


        I'm not really a human, but I play one on earth My Petition to the Great Cosmic Conciousness