Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Verbose Tk Code

by LanX (Sage)
on Feb 28, 2021 at 18:22 UTC ( #11128912=perlquestion: print w/replies, xml ) Need Help??

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

Hi

I was meditating about designing DSLs and especially how to robustly implement with like in JS to a have clearer and less verbose code.

So I thought to use Tk as a use case and found this 20 year old https://www.perl.com/media/_pub_1999_10_perltk_index/sld001.htm demo.

The code is extremely verbose and I'm asking myself if this is really state of the art.

For instance the code from https://www.perl.com/media/_pub_1999_10_perltk_index/sld010.htm

Is it really necessary to always repeat ...

  • -background => 'cyan'
  • ->pack()
  • and finally my $t1 looks like sloppy code which will generate warnings

my $t1 = $left1->Label(-text => ' ', -background => 'cyan')->pack(); my $t1 = $left1->Label(-text => ' Day ', -background => 'cyan')->pack(); my $t1 = $left1->Label(-text => ' Week ', -background => 'cyan')->pack(); my $t1 = $left1->Label(-text => ' Month ', -background => 'cyan')->pack(); my $t1 = $left1->Label(-text => ' Year ', -background => 'cyan')->pack();

If needed I'd rather implement a helper like:

with ($left1) { default -background => 'cyan'; chain { pack() }; Label(-text => ' '); Label(-text => ' Day '); Label(-text => ' Week '); Label(-text => ' Month '); Label(-text => ' Year '); }

(pseudocode)

But before reinventing the wheel I'd like to discuss my doubts. =)

Cheers Rolf
(addicted to the Perl Programming Language :)
Wikisyntax for the Monastery

Replies are listed 'Best First'.
Re: Verbose Tk Code
by Corion (Patriarch) on Feb 28, 2021 at 18:28 UTC

    Why not have

    my %default = (-background => 'cyan'); sub make_label( $parent, %attrs ) { $parent->Label( %default, %attrs )->pack } make_label(-text => ' ' ); ...

    I don't see a need to involve a DSL here when simple subroutines and global(-ish) variables can achieve the same.

      probably, but my question was rather at Tk experts if that's even still necessary.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

      PS: zentara where are you? :)

        Hello LanX,

        I'm obviously not an expert, but waiting for zentara to awake from his oak you can have my humble opinion.

        Corion is right: if you have a case where repetition is needed providing some %default is better tan nothing and suffices.

        The real problem is that writing a GUI is verbose. My best Tk program is tartaglia's triangle experiment. The interface is clear for my taste but as you can see there are ~70 lines of pure verbose Tk code. Ok I have a lot of -side => 'left',-expand => 1,-padx=>5 but it adds nothing to me if I have some shortcut for this, not even in the Corion's simple way.

        Why? Because I generally cut and paste my own code.

        Then you can have very sligth differences: nowadays I'm playing with yet another Tk and GD application: look at its init_control_window sub: I'm packing bigger frames with the border and then inside them I put othere smaller frames expanding horizontally, but first and last subframes also need -pady => 5 to look nicer. In the same way if you want something to be packed at -side => 'right' and you want it to not be too much to the border, then the first element (right aligned elements must be packed in reverse order) need a different -padx value. And so on.

        So in my humble opinion Tk is already complicated this way and some shortcut as in your example can make it even less redable.

        L*

        There are no rules, there are no thumbs..
        Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

        I've lately been doing things like this

        $mw->Button(-text => $_->[0], -command => $_->[1], -font => 24, )->pack( -side => 'left', -fill => 'x', -expand => 1) for [Restart => \&restart], ['Previous State' => \&previous], ['Random Move' => \&random], [Help => \&help], [Exit => sub {$mw->destroy}];

        which is a section from my chess player on RosettaCode https://rosettacode.org/wiki/Chess_player#Perl and I probably would do the same kind of thing for those five Labels.

        One might consider for to be a perl approximation of with :)

Re: Verbose Tk Code
by kcott (Archbishop) on Mar 01, 2021 at 10:21 UTC

    G'day Rolf,

    I completely understand where you are coming from. I write a lot of Tk code. I've posted a fair amount on this site; however, I tend to completely ignore presentation in those cases, focussing mainly on pure functionality (questions are typically along the lines of "How do I make X do ...", as opposed to "How do I make X look like ..."). On the other hand, for my personal code, presentation is important; over the years, I've come up with a variety of techniques to circumvent verbosity and repetition.

    For any GUI application, you want a consistent look and feel. This is something that should be specified once (DRY principle). Take a look at the option database, Tk::option, to achieve this. You can set up colours, fonts, etc. globally, e.g. *background; override that for specific types of widgets, e.g. *Button*background; and even override that for individual widgets using the Name option (I've never needed that but it is documented).

    With that alone, code that previously looked like:

    $parent->Widget( -background => ..., -foreground => ..., ... other option/value pairs ... -padX => ..., -padY => ..., )

    would now be simply:

    $parent->Widget()

    If you do decide to use the option database, I'd strongly recommend that you do use the optional 'priority' and set it 'startupFile'. This will allow: hard-coded widget defaults to be used where you haven't defined something different; user preferences (e.g. from .Xdefaults) to override your choices; and, the ability to have fine-grained control override everything (e.g. a Label with an error message might want emboldened, red text).

    Because option/value pairs are just a list, you can reduce

    -optionA => valueA, -optionb => valueB, -optionC => valueC

    to

    qw{-optionA valueA -optionb valueB -optionC valueC}

    Obviously, that doesn't work where values are variables, coderefs, strings with spaces, and so on; however, overall, I find that to be generally usefully. And, of course, you can combine the two styles:

    qw{-option1 value 1 ... -optionN valueN -command}, sub { ... }

    Automatically adding ->pack() is probably a bad idea; I've never done it but it should be possible (perhaps with subclassing to give, for example, a PackedButton widget). In a number of cases you'll want to separate the widget creation and geometry management operations with intervening code; automatically adding ->pack(), ->grid(), etc. is problematic in these instances.

    The options passed to geometry management methods are likely to be mostly identical to achieve a consistent look and feel. Again, apply the DRY principle. There a number of ways to do this; I typically use something like this:

    ... sub _std_btn_pack_opts () { return (-side => left, -padx => 5, -pady 2); } ... $parent->Button(...)->pack(_std_btn_pack_opts); ...

    You can, of course, add options and override the standard ones, e.g.

    $parent->Button(...)->pack(_std_btn_pack_opts, -anchor => 'n', -side = +> 'right');

    [Note: I have researched adding geometry management options to the option database but haven't been able to find a way to do that. If it is at all possible, I'd be very happy to hear about it.]

    It's unclear what you were trying to demonstrate with five 'my $t1' instances in the same lexical scope. Unless you need to subsequently reference a widget, there is no point in assigning it to a variable. Here's a rough example showing where assignments would be needed.

    my $frame = $parent->Frame()->pack(); # NEEDED $frame->Label(-text => 'Status Control')->pack(); # NOT NEEDED my $status1 = $frame->Label(-text => $statmsg1)->pack(); # NEEDED $frame->Label(-textvariable => \$statmsg2)->pack(); # NOT NEEDED $frame->Button( -text => 'Update Status 1', -command => sub { $status1->configure(-text => $new_statmsg1); }, )->pack(); # NOT NEEDED

    Update: I added '->pack()' to the last statement in the last example ($frame->Button(...)). Not really needed for the example; just keeps things consistent.

    — Ken

Re: Verbose Tk Code
by kcott (Archbishop) on Mar 01, 2021 at 12:38 UTC

    Me again :-)

    I had multiple interruptions when composing my original response, "Re: Verbose Tk Code"; I think I finally posted it three or four hours after starting it. I kept thinking that I'd missed something but couldn't tell what that was. Now I see it was the 'with ($left1) {...}' pseudocode.

    Firstly, a couple of notes. I wouldn't use spaces for padding text; there are a number of '*pad*' options that would be a better choice, also look for other options, such as '-anchor' and '-sticky', which may be useful. If you want to align widgets vertically, grid() is a better choice than pack(); I don't know if that was your intent here.

    Using my earlier suggestions, and the notes above, your pseudocode could be implemented as simply as:

    $left1->Label(-text => $_)->pack() for '', qw{Day Week Month Year};

    If you haven't used the option database, add "-background => 'cyan'" to the line above. You'll probably want some pack options like "-padx => 5, -anchor => 'w'" (but that's a complete guess as I don't know what layout you're aiming for).

    As a more general solution, where you perhaps have complicated options and wanted to store the objects created for later use, something along these lines:

    my @widget_order = qw{blank abc xyz}; my %widget_data = ( blank => { opts => ['-text', ''] }, abc => { opts => ['-text', 'Apple Banana Cherry'] }, xyz => { opts => ['-text', 'X Y Z'] }, ); for my $widget (@widget_order) { $widget_data{$widget}{object} = $parent->Widget(@common_options, $widget_data{$widget}{opts}, )->geo_mgr(@geo_mgr_opts); }

    That's obviously very rough and incomplete but hopefully gives you an idea.

    See also the Widget Demo. The Radiobutton example has a number of blocks of code along the same lines as I have here.

    — Ken

Re: Verbose Tk Code
by GrandFather (Saint) on Mar 01, 2021 at 00:30 UTC

    GUI code generally ends up with lots of similar looking code to manage layout tasks. Often that can be tided up with helper functions that lay out a row of controls or set up a table row or whatever the task might be. Using hashes for common parameter sets helps a lot too. Often tossing display elements into an array the running over the array to get stuff packed in the right order helps too.

    At the end of the day the techniques you use to clean up repetitive Tk code are much the same as the techniques you use to clean up any repetitive code - refactor the repetitive bits, reduce, reuse, recycle.

    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
Re: Verbose Tk Code
by jcb (Parson) on Mar 01, 2021 at 04:48 UTC

    As another monk mentioned, you can create the widgets, place them into an array and then apply geometry management to the array elements, but the packer is really a particularly simple case — the other geometry managers end up needing more information, often unique to each widget.

    As for -background => 'cyan', Tk offers an "options database" that uses something similar to app-defaults values and the options database should be used in preference to hardcoded color schemes. I am still learning how to use this and would appreciate any help other monks can provide for all here, but here are some bits from a program I am working on: (partially developed; partially tested; this is from the program I mentioned in POE::Filter for Tor control protocol)

    use FindBin qw($Bin); # ... $poe_main_window->configure(-title => 'Tor Monitor'); $poe_main_window->appname('TorMon'); $poe_main_window->iconname('TorMon'); $poe_main_window->optionReadfile(File::Spec->catfile($Bin, 'app-defaul +ts'), 'startupFile'); $poe_main_window->configure (-background => $poe_main_window->optionGet('TorMon.background', 'Top'));

    $Bin/app-defaults:

    ! Main TorMon.background: ghost white

    So far, I have had no trouble applying defaults from an options file to all other widgets created (use "*" instead of "." in the options file if you want a setting to apply to a widget's descendants), but the main window itself just does not seem to "pick up" the settings from the options database. Lines 9 through 11 in the example is the workaround that I have been using. Maybe this is an interaction between Tk and POE?

    You may also find the Name and -class options to be useful here; they are analogous to the id and class attributes in HTML if you consider the options database analogous to a stylesheet.

    Lastly, setting the iconname on the main window is important — some window managers can crash while manipulating windows that do not set an icon name.

      re: app-defaults: that sounds exactly like ~/.Xdefaults -- yet another DSL.

        It basically is — the same format is used for both defaults (app-defaults files in traditional Xaw programs) and customizations (.Xdefaults and the "resource database"). As far as I can tell, Tk picked it up from Xrm, and there actually is an option to have Tk use Xrm but I have not looked into that yet.

      setting the iconname on the main window is important some window managers can crash while manipulating windows that do not set an icon name.

      I find that rather hilarious, not funny but ridiculing-the-wm kind.

Re: Verbose Tk Code
by Anonymous Monk on Mar 03, 2021 at 00:47 UTC
A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11128912]
Approved by marto
Front-paged by kcott
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (6)
As of 2022-12-01 07:16 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Notices?