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

Background: I've got an array (@bit) that represents a bit field. I would like to have a menubutton whose text is the concatenation of elements 9 and 10 of this array. I want the menubutton's text to update whenever I change the values of elements 9 and 10. So the button might display 01, and then after changing bits 9 and 10 it would display 11, or 10, etc.

Here is a snippet of the code I have right now:
(I've tried a couple of variations with no luck)
$bit_10 = \$bit[10]; $bit_9 = \$bit[9]; $bit_10_9 = $$bit_10 . $$bit_9; $bit_10_9_mb = $r2Frame->Menubutton( -textvariable => \$bit_10_9, -menuitems => [['command' => "00", -command => sub {$bit[10] += 0; $bit[9] = 0;}], ['command' => "01", -command => sub {$bit[10] += 0; $bit[9] = 1;}], ['command' => "10", -command => sub {$bit[10] += 1; $bit[9] = 0;}], ['command' => "11", -command => sub {$bit[10] += 1; $bit[9] = 1;}]], -background => "black", -foreground => "white", -tearoff => 0, -relief => "groove", )->pack(-side => "left", "-after" => $bit_ +11_entry);
Right now the code does indeed display the concatenation of the bits, but the button's text is never updated
from its original value whenever I change the values of bits 9 and 10.

I'm stuck! I have tried this a couple of different ways (with and w/o references) and can't get it to work.
Any help would be greatly appreciated.

Replies are listed 'Best First'.
(ichimunki) Re: Updating concatenated text on a Menubutton
by ichimunki (Priest) on Nov 27, 2001 at 02:17 UTC
    Your subs don't include anything that would alter the contents of $bits_10_9. The reason a reference works for the -textvariable piece is that it constantly checks that variable when doing screen-draw updates, but your assignment to $bit_10_9 is only checking those references once (when that statement is executed). You might try making a setbits() sub and using that as your callback. Example:
    my $bit_10_9; #yes, this is a horrible abuse of globals setbits( 0,0 ); my $bit_10_9_mb = $r2Frame->Menubutton( -textvariable => \$bit_10_9, -menuitems => [['command' => '01', -command => sub{ setbits(0,1); }] .... then sub setbits { $bit[10] = shift || 0; $bit[9] = shift || 0; $bit_10_9 = $bit_10 . $bit_9; }
    Now normally I try to keep these sorts of globals to a minimum. But without going to OO data structures, it can be difficult to make this sort of data available to callbacks.

    updateSheesh, I give the sloppiest answers sometimes. I've fixed this to apply more readily to the situation at hand.

      I'm not familiar with this GUI system, but does the subroutine in the -command argument get passed anything in @_? Maybe that can be passed into setbits() where the function can manipulate the menu's text directly, instead of through these global references.

      Just some idle thoughts... I also don't care for the intermediate variables $bit_9 and $bit_10. He should probably just be using @bit[9,10] directly.

(ar0n: tie) Re: Updating concatenated text on a Menubutton
by ar0n (Priest) on Nov 27, 2001 at 03:52 UTC
    I believe you might want to check out the tie function. You could tie @bit to a package with (amongst others) a STORE subroutine, that checks whether you're altering elements 9 or 10. Check out the perltie documentation, it's quite illuminating.

    [ ar0n -- want job (boston) ]

Re: Updating concatenated text on a Menubutton
by Kahlua_II (Initiate) on Nov 27, 2001 at 04:39 UTC
    Thanks for the help thus far, but I think that I failed to fully explain my problem. I want the text on the menubutton to change WHENEVER bit 9 or bit 10 changes. Not only when bit 9 or bit 10 is changed by the menubutton itself, but also when I change the value of bit 9 or bit 10 at other points in my code.

    Do I need to change
    -textvariable => \$bit_10_9,

    to something else?

    Everything works fine (i.e. the text changes WHENEVER bit 9 is changed) when I use only
    -textvariable => \$bit[9],
    or
    -textvariable => \$bit[10],

    If it works fine for one bit....how can I make it work for a concatenation of two bits?
    I don't want to have to update another variable like $bit_10_9 everytime I change $bit[10] or $bit[9].
      (Updated) I certainly agree with ar0n that tie is the way to go-- unless that looks too complicated for the case at hand.I still think the best way would be to build something similar to an If you don't want to do that, you might try an accessor method. That way you can trap the calls that set the bits and update the display variable whenever the bits get set. But I tried to do something like making the -textvariable an anonymous sub to no avail. I ended up with debugging code in my label.
      #!/usr/bin/perl -w use strict; use Tk; my $Foo = 'x'; my $Bar = 'y'; my $foobar = setfoobar(); my $mw = Tk::MainWindow->new(); my $entry = $mw->Label( -textvariable => \$foobar, )-> pack( -expand => 'y', ); my $foo_button = $mw->Button( -text => "FOO", -command => sub { flip( $Foo );} )->pack(); my $bar_button = $mw->Button( -text => "BAR", -command => sub { flip( $Bar );} )->pack(); MainLoop(); sub flip { $_[0] = ( $_[0] eq 'x' ) ? 'y' : 'x'; setfoobar(); } sub setfoobar { $foobar = $Foo . $Bar; }
      Here is my sample code reworked to use a tied array (not all the necessary features of a tied array are implemented here FETCHSIZE and STORESIZE may not be very useful as written-- I just wanted to try the basics of tie and get this example to work a little less clumsily). Thanks to ar0n for suggesting tie in the first place, to ar0n and broquaint for helping me fix a bug in this code. Note that you can also export the $Concat variable in the package to avoid having to package qualify it in Main, but I think I prefer it how it is.
      #!/usr/bin/perl -w use strict; use Tk; my @bit; tie @bit, 'Bitters'; my $mw = Tk::MainWindow->new(); my $entry = $mw->Label( -textvariable => \$Bitters::Concat, )-> pack( -expand => 'y', ); my $foo_button = $mw->Button( -text => "Bit One", -command => sub { flip( $bit[1] );} )->pack(); my $bar_button = $mw->Button( -text => "Bit Zero", -command => sub { flip( $bit[0] );} )->pack(); print "MainLoop\n"; MainLoop(); sub flip { $_[0] = ( $_[0] eq 'x' ) ? 'y' : 'x'; } { package Bitters; use vars qw( $Concat ); sub TIEARRAY { my $class = shift; my $self = { ARRAY => ['x','x'] }; bless $self, $class; $self->update_concatenation(); return $self; } sub FETCH { my($self,$idx) = @_; return $self->{ARRAY}[$idx]; } sub FETCHSIZE { my $self = shift; return $self->{ARRAY}+0; } sub STORESIZE { print "STORESIZE ignored\n"; } sub STORE { my($self, $idx, $value) = @_; print "[STORE $value at $idx]\n"; $self->{ARRAY}[$idx] = $value; $self->update_concatenation(); return $self->{ARRAY}[$idx]; } sub update_concatenation { my $self = shift; $Concat = $self->{ARRAY}[0] . $self->{ARRAY}[1]; print "$Concat\n"; } }#end_package Bitters;