http://www.perlmonks.org?node_id=1007349

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

Happy holidays, monks and monkettes!

I am working on a new project and ran into a bit of a snag, again. This time, I have a bunch of Scale (slider) widgets lined up vertically next to each other. At the top of each slider bar I have the label super-imposed on the bar as an image of some text I rotated 90 degrees counter clockwise for each.

In order to have this label not obscure the slider grab-thingy I have written a simple little subroutine. When the slider reaches a certain height on a given bar (depending on how long the text label is, the label will snap to below it using the form() manager to relocate the associated image, and move with it using a constant re-position routing in the slider's command subroutine.

This works perfectly when moving sliders.

The problem I am having is getting the position of slider labels to update when I change their variables from another section. I want the command associated with each slider to still run and move the label out of the way when it does even if I don't slide them by hand.

In the past I have had no problem getting widgets to update through other functions, either by changing their variables directly, running subs, using update, or sometimes configure(). None of these methods are working this time, so I am wondering what is the best way to scoot stuff around.

Would forgetting the images before re-forming them make any difference? The sliders do actually move, however the command associated with them appears to not run unless you physically move them by hand.

So, my question is how do you get a slider's command to run even when it is not being moved by hand (other function alters its variable, etc.) Or, how can I use the form() geometry manager to dynamically adjust the position of a widget currently off-screen and have it update reliably?

Replies are listed 'Best First'.
Re: Dynamic re-pack widget
by thundergnat (Deacon) on Dec 05, 2012 at 19:10 UTC

    Since you don't show any code, it is difficult to point out where you might need to make changes to accomplish your goal. That being said... I don't think your issue is the form geometry manager particularly. If it works as you expect when you use the slider to adjust it, then it should also work if you set it using the built in $scale->set() method. That will fire any attached command routines. See demo below. Note that the tied variable $w{size} holds the value of the scale. If you change $w{size} directly, it won't fire the command. You need to use $w{scale}->set.

    use warnings; use strict; use Tk; my %w; $w{size} = 90; $w{mw} = MainWindow->new; $w{mw}->Label( -text => 'Let it snow!' )->pack; $w{frame} = $w{mw}->Frame(-borderwidth => 10)->pack; $w{scale} = $w{frame}->Scale(qw/-orient vertical -length 284 -from 120 + -to 10 /, -command => sub { $w{label}->configure( -font => "{Arial} $w{size} +") }, -variable => \$w{size} )->pack(-side =>'left'); $w{label} = $w{frame}->Label( -text => "\x{2603}", -font => "{Arial} $w{size}" )->pack(-side =>'right'); $w{frame}->Button( -text => "${_}pt.", -width => 4, -command => [ sub { $w{scale}->set($_[0]) }, $_ ] )->pack() for qw/20 40 60 80 120/; MainLoop;

    For a contrast, change the button command to:

    [ sub { $w{size} = $_[0] }, $_ ]
Re: Dynamic re-pack widget
by Phinix (Acolyte) on Dec 05, 2012 at 19:29 UTC

    That is actually what I ended up doing for now, but it is an imperfect solution for an imperfect world. I bound each of my sliders to a subroutine that basically handles setting the label where it belongs on mouse-over.

    What is really driving me batty is this seems to be some sort of update snag. It may have something to do with Notebook. My sliders are on one notebook page and my changes are being made on another.

    I tried binding the notebook tabs themselves, so that when a user clicked back over to my slider page, the changes would be updated. What is really weird is that this worked, but only if you click to the slider tab, then click that tab again.

    It is like the updates are not effected until they are called from the notebook tab they need to be effecting. Very annoying. Not even set() is taking effect.

    For now this works, but I know I will bash my brains against this one until I can figure it out. I will try and post some code later this afternoon when I get back to it.
Re: Dynamic re-pack widget
by Phinix (Acolyte) on Dec 05, 2012 at 20:11 UTC

    I was right! This was a "feature" of the Notebook widget. What ended up solving it, was to add -raisecmd => sub { &sliders; } to the line that added the Notebook page with my sliders on it. Then in the "sliders" subroutine I make all the changes to what needs updating.

    Quite gratifying! =P
Re: Dynamic re-pack widget
by Anonymous Monk on Dec 05, 2012 at 19:17 UTC
    Sounds horrible -- sounds like you want to write your own geometry manager but are reluctant to do it :) every action creates an event, and if there are handlers (callbacks, subroutines ) bound to that even ( Tk::bind ) they get triggered, so you can make one with eventGenerate or invoke the callback directly, whatever you need to do -- gui loops, they're just state machines that push messages on a que