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

Greetings, O wise ones.

I am working on a Tk interface for my very first Useful Program[1]. The main window contains several frames which contain very similar widgets. I want to be able to pack/packForget the frames dynamically, so to improve readibility I would like each frame to have a descriptive name. I.e. not the frames should be elements of an array or something similar.

On the other hand, since the widgets inside each frame are so similar, I thought it would make sense to pack the widgets in a loop, packing one frame per cycle. I basically have a list of elements I wanted to iterate over, so a foreach loop seemed ideal. My first (and so far only) idea was to do something like this:

use strict; use Tk; my $mw = MainWindow->new; my $hello_string = "Hello"; my $silly_string = "Very Silly!!!"; my $frame1 = $mw->Frame(); my $frame2 = $mw->Frame(); my $frame3 = $mw->Frame(); my $frame4 = $mw->Frame(); my %frame_var_hash = ($frame1 => {-text => $hello_string, width => 7}, $frame2 => {-text => $silly_string, width => 9}, $frame3 => {-text => "XXXXX", width => 35}, $frame4 => {-text => $silly_string, width => 7}); foreach my $cur_frame (keys %frame_var_hash){ $cur_frame->Label( $frame_var_hash{$cur_frame}, -expand => 1, )->pack(); } [...] MainLoop;

I thought this was fairly clever (I impress myself easily) but Perl disagreed:

Can't locate object method label via package "Tk::Frme=HASH(0x286D960)" (perhaps you forgot to load "Tk::Frme=HASH(0x286D960)"? at test line 27.

My reading of the camel book suggests that the loop variable takes on the identity of whatever list element is assigned to it. For example,

 foreach $element (@some_array){$element++}

actually increments the values in $some_array. Apparently this doesn't hold in a straightforward way for objects. I tried de-referencing $cur_frame, I tried using references to the frame objects as the keys in %frame_var_hash and then de-referencing $cur_frame, and I think I tried some other things but it was late and I forget.

Is there a way to make a loop like this work? Is there a better way to solve the problem? I am seated at you feet, listening.

-- Fuzzy Frog

[1] The guts have been working for almost two years. I just want to give it a pretty face. The person who will be taking over my job has never heard of a command line.

Edited by Chady -- escaped barackets and made them into a nice footnote link.

Replies are listed 'Best First'.
Re: Iterating over objects
by japhy (Canon) on May 18, 2004 at 23:03 UTC
    As BUU said, hash keys are only strings; you can't get the reference back out of them. That said, why not use an array of the objects and their argument list?
    my @data = ( [ $frame1 => {-text => $hello_string, width => 7} ], [ $frame2 => {-text => $silly_string, width => 9} ], [ $frame3 => {-text => "XXXXX", width => 35} ], [ $frame4 => {-text => $silly_string, width => 7} ], ); for my $set (@data) { my ($frame, $settings) = @$set; $frame->Label( %$settings, # I'm pretty sure you want %$settings -expand => 1, # not just $settings... )->pack();
    _____________________________________________________
    Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

      Thank you. I started thinking along these lines when I read BUU's message. Once /s?he/ mentioned it I rembered reading about stringification of hash keys. The problem is that printing a variable that contains a stringified reference yeilds exactly the same thing as printing a variable that contains an actual reference.

      I'm actually leaving tomorrow for a short vacation, so I won't be able to try this out for a few days. I had to bet some feedback on my problem before I left, though. Ohterwise I wouldn't stop thinking about it.

      _______________________________________________________________________
      Two messages found in the same fortune cookie:
      -Friends ask for your time, not your money.
      -Time is money
Re: Iterating over objects
by BUU (Prior) on May 18, 2004 at 22:12 UTC
    It's fairly simple, and documented some where (probably in the docs for hashes), hash keys are stringified and you can not get the actual object back from a stringified reference.
Re: Iterating over objects
by dimar (Curate) on May 18, 2004 at 22:58 UTC

    Hiya Frog,

    A Related Module For Ya

    This does not answer your question, but I have a module that works like a 'generic argument grabber' using Tk for exactly the kind of scenario you mentioned. Read further if you (or anyone else) are interested.

    The Scenario

    You already have a working perl script and you simply want a fast, easy, and flexible way to supply arguments to that script in a 'novice-user-friendly' 'point-and-click' kinda way.

    The Background

    If you are familiar with VBScript or JavaScript, you probably have heard of the *input box* function, which simply pops up a quick-and-dirty text box for user input. This is useful, but it suffers many limitations:

    • It only allows a 'textbox' input control
    • It only allows a single input control
    • It offers no flexibility for 'morphing' the input dialog

    My Module: Synopsis

    Suppose you have a script 'CreateNewEmployee.pl' that requires some input arguments, and you want an input dialog to let the novice user supply the arguments. Here is some sample code ...

    ### Example 000 my $get_args = {}; $get_args->{fname} = ''; $get_args->{lname} = ''; $get_args->{title} = ''; $get_args->{dept } = ''; my $usr_input = &TkDataDialog($get_args,'data_rec'); &DoCreateTheEmployeeFunction( $usr_input->{fname}, $usr_input->{lname}, $usr_input->{title}, $usr_input->{dept }, );

    This example pops up a dialog that lets the user supply the specified values in a predictable looking input form. You can also use compact syntax to accomplish the same thing.

    ### Example 001 (compact syntax) my $usr_input = &TkDataDialog( 'fname lname title dept', 'fld_list',); &DoCreateTheEmployeeFunction( $usr_input->{fname}, $usr_input->{lname}, $usr_input->{title}, $usr_input->{dept }, );

    My Module: Motivation

    I wrote this to solve exactly the kind of problem you are talking about, and because I couldn't find anything on CPAN that matched my requirements closely enough.

    Feature Highlights

    • Ability to specify Input Dialog using alternate syntax styles (eg XML, 'CSS-similar', quote words, perl hashref)
    • Ability to specify any number of input fields
    • Ability to specify different types of input controls (default is the plain-vanilla input box, but you can also use 'select list' or 'textarea')
    • Ability to specify Caption (aka Field label, aka 'friendly name'), Default Value, and Preset Option List for each input field
    • Ability to specify the order in which the input fields should appear on the input form
    • Ability to do all this stuff in a single function call without having to learn Tk

    Feature Lowlights

    • No ability to control the placement of widgets within the frame
    • No ability to add 'tooltips' or help that show the user the semantics behind each input field. (although if I knew enough Tk I'd put this in)

    This code is just for those cases where you want a quick and dirty way to get input of a simple data record (aka name value pairs) and you dont really care about the exact appearance of the Tk window.

    My Module: Dependencies

    use Tk; use Tk::BrowseEntry; use Tk::DialogBox; use XML::Twig; ## to support xml-dialog syntax format use CSS::Tiny; ## to support css-like dialog syntax use Data::Dumper; ## only used for debugging and tests
Re: Iterating over objects
by Fuzzy Frog (Sexton) on May 18, 2004 at 21:55 UTC

    I am (was?) the OP. I think I switched browser windows without logging back in. Grrrrrrr*.

    -- Fuzzy Frog

    *Yes, frogs can growl.