http://qs321.pair.com?node_id=524951

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

Please excuse my terminology.

I've been working on a cut down mock up of an app.

At it's core is a Tk front end which calls and receives input from other methods/subs.

It's this circularity that has been giving me grief. In particular, I'm passing the Tk object to subs that need it which feels wrong somehow.

How would other monks approach this?

#!/usr/bin/perl # auto.pl use strict; use warnings; use Auto::GUI; use Auto::MakeArticle; my $auto = Auto::GUI->new; $auto->fire_up_front_end or die;
package Auto::GUI; use strict; use warnings; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } sub fire_up_front_end{ my $self = shift; # display the gui # later... a button will call MakeArticle my $mk = Auto::MakeArticle->new; $mk->make($self); # pass the GUI object (?) } sub msg { # show progress/error messages my $self = shift; my $msg = shift; print "$msg\n"; } 1;
package Auto::MakeArticle; use strict; use warnings; sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } sub make{ my $self = shift; my $gui = shift; # GUI object # do stuff to make page my $msg = "message from MakePage"; $gui->msg($msg); } 1;
The output is as expected:
---------- Capture Output ---------- > "C:\Perl\bin\perl.exe" auto.pl message from MakePage > Terminated with exit code 0.

Replies are listed 'Best First'.
Re: OO circularity
by g0n (Priest) on Jan 23, 2006 at 16:13 UTC
    I know what you mean. It gets even more gruesome when you want to dynamically recompose your GUI (changing labels, changing the input widgets available according to the setting of a dropdown etc) as you have multiple objects to pass to subroutines. E.g. if you want to change the labels on 5 text fields, and remove 2 other textfields and their labels, based on a drop down setting, you have 9 separate widgets to update, and unless you start doing fiddly things like getting a list of children from the parent, you have to pass them all in).

    I tend to put the Tk object itself, and widgets that I know I'm going to have to twiddle, in a hash called something like %tkObjects, and pass that to any piece of code that requires GUI update functionality. If you're coding from an OO perspective, you can make that hash an attribute of an object, and consider that the object 'has a gui'. You then end up with code that looks like:

    sub update_button { my $self = shift; my $buttonobj = $self->{tkobjects}->{submitbutton}; $buttonobj->#do something with the button }

    From a non OO point of view, you might consider that your subroutines are doing two things: doing the necessary processing, and updating the GUI.

    That technique is on dodgy ground for the principle of narrowest scope for variables & objects, but conforms with laziness and impatience nicely :)

    Alternatively (and I hate myself for saying this), you could make certain very carefully chosen objects global.

    I'm certain there are better ways, but those are the two I've used.

    Update: You could also put all the GUI construction and update in one place, and update the UI according to the return values of subroutine calls. That way is perhaps neater from a design point of view, but can rapidly end up with a really huge main gui function that's difficult to maintain. So thats a third, equally vile, approach that I've tried.

    --------------------------------------------------------------

    "If there is such a phenomenon as absolute evil, it consists in treating another human being as a thing."

    John Brunner, "The Shockwave Rider".

Re: OO circularity
by Roy Johnson (Monsignor) on Jan 23, 2006 at 17:24 UTC
    Have the non-GUI code return the message and have the GUI code act on it.
    # I changed the last line in this Auto::GUI sub sub fire_up_front_end{ my $self = shift; # display the gui # later... a button will call MakeArticle my $mk = Auto::MakeArticle->new; $gui->msg($mk->make); # return to the GUI object } # And changed this Auto::MakeArticle sub sub make{ my $self = shift; # do stuff to make page return "message from MakePage"; }

    Caution: Contents may have been coded under pressure.
Re: OO circularity
by Fletch (Bishop) on Jan 23, 2006 at 15:46 UTC

    Class::Singleton? Or pass it once to Auto::MakeArticle for the constructor and have it stored in the instance (i.e. there's a hasa relationship).