Category: GUI
Author/Contact Info Fred Cohen all.net
Description: You create forms that take in data and provide you with the answers, one line per entry / select area. Runs from a command line - real simple to use.
#!/usr/bin/perl -w
# Form boxes, build forms - get answers (C) - Fred Cohen 2004 - Distri
+buted with no warranty
# example:
# formbox.pl fill label test fill crlf radio "one two three four" crlf
+ label this entry 23 "this is sad" crlf fill label that \

# NOT RIGHT     hr      fills width with horizontal bar
# NOT YET       sub form        pop up a new form within this form
# NOT YET       display [integer] create an output area integer lines 
+long - the width of the rest of the world
# NOT YET       show    [integer] [pathname] show pathname in display 
+number integer
# NOT YET       print   [integer] [statement] show results of eval in 
+display number [integer]
#
#syntax entry   [integer] [default]     entry 20  'this example'      
+          Create entry area
#syntax entry   [fill] [default]        entry fill 'this example'     
+          Entry with available width
#       label   [value]                 label 'test label             
+          Add a label
#       fill                            fill                          
+          fill width
#       crlf                            crlf                          
+          next line (frame)
#       OK                              OK                            
+          OK button
#       hr                              hr                            
+          Horizontal rule
#       check   [word]+                 check 'affirm entry'          
+          Checkbox
#       mark    [word]                  mark 'affirm entry'           
+          Mark box
#       list    [word]+                 list 'one two three four five'
+          List box with these options
#       radio   [word]+                 radio 'one two three four five
+'         Radio with these buttons
#       slide   [integer] [integer]     slide 12 34                   
+          slider from 12 to 34 or whatever
#       eval    [word]  [statement]     eval 'add' '$z=2+3+4;print "=$
+z\n";'    evaluate statement when button pushed
#       file    [pathname]              file /root/testfile           
+          add file to the end of pending commands

require 5.002;
use English;
use Tk;
use Tk::DialogBox;

my $ans="";
$debug=0;
$fcount=1;
$count=0;
$evalcount=0;

sub done {for ($i=1;$i<=$count;$i++) {$ans=eval "\$answer$i";chomp $an
+s; print "$ans\n";} exit;}
sub nextword {$w=pop @todo;return $w;}

sub godoit {while ($todo[0])
        {$word=nextword;
        if ($word eq "list")
                {if ($debug) {print "List-";}
                $count++;
                eval "\$answer$count=\"\";";
                $things=nextword;
                @things=split(/\s/, $things);
                my $thingtoeval="\$textframe$fcount->Optionmenu(-optio
+ns => [";
                foreach $item (@things) {$thingtoeval=$thingtoeval."'$
+item',";}
                $thingtoeval=$thingtoeval."], -variable => \\\$answer$
+count) ->pack(-side => 'left', -fill => 'x');";
                eval $thingtoeval;
                }
        elsif ($word eq "radio")
                {if ($debug) {print "Radio-";}
                $count++;
                eval "\$answer$count=\"\";";
                $things=nextword;
                @things=split(/\s/, $things);
                foreach $item (@things)
                foreach $item (@things)
                        {eval "\$textframe$fcount->Radiobutton(-text =
+> '$item', -variable => \\\$answer$count, -value => '$item')
                                ->pack(-side => 'left', -fill => 'x');
+";}
                }
        elsif ($word eq "mark")
                {if ($debug) {print "Mark-";}
                $count++;
                $things=nextword;
                eval "\$answer$count=0;";
                eval "\$textframe$fcount->Checkbutton(-text=> '$things
+', -indicatoron=> \$answer$count, -variable => \\\$answer$count)  
                         -> pack (-side => 'left');";
                }
        elsif ($word eq "check")
                {if ($debug) {print "Check-";}
                $count++;
                $things=nextword;
                eval "\$answer$count=0;";
                eval "\$textframe$fcount->Checkbutton(-text=> '$things
+', -indicatoron=> \\\$answer$count, -variable =>
                        \\\$answer$count) -> pack (-side => 'left');";
                }
        elsif ($word eq "slide")
                {if ($debug) {print "Slide-";}
                $count++;
                $from=nextword;
                $to=nextword;
                eval "\$answer$count=0;";
                eval "\$textframe$fcount->Scale(-from=> $from, -to => 
+$to, -showvalue => '1', -orient => 'horizontal', -variable => 
                         \\\$answer$count) -> pack (-side => 'left', -
+fill => 'x', -expand => '1');";
                }
        elsif ($word eq "label")
                 {if ($debug) {print "Label-";}
                $things=nextword;
                eval "\$textframe$fcount->Label(-text => \$things)->pa
+ck(-side => 'left');";
                }
        elsif ($word eq "eval")
                {if ($debug) {print "Eval-";}
                $evalcount++;
                $default=nextword;
                $things=nextword;
                eval "sub sub$evalcount {$things};";
                eval "\$textframe$fcount->Button(-text => \$default, -
+command => sub {sub$evalcount;})->pack(-side => 'left', -expand   
                         => '1', -fill => 'both');";
                }
        elsif ($word eq "OK")
                {if ($debug) {print "OK-";}
                $evalcount++;
                eval "\$textframe$fcount->Button(-text => \"OK\", -com
+mand => sub {done;})->pack(-side => 'left', -expand => '1',
                        -fill => 'both');";
                }
        elsif ($word eq "fill")
                {if ($debug) {print "Fill-";}
                $things="";
                eval "\$textframe$fcount->Label(-text => \" \")->pack(
+-side => 'left', -expand => '1');";
                }
        elsif ($word eq "hr")
                {if ($debug) {print "Hr-";}
                $things="";
                eval "\$textframe$fcount->Text(-text => \"-\")->pack(-
+side => 'left', -expand => '1', -fill => 'x');";
                }
        elsif ($word eq "crlf")  
                {if ($debug) {print "Crlf-";}
                $fcount++;
#       unbelieveably - frames must explicitly be built in the global 
+context and cannot be eval'ed here
#               eval "\$textframe$fcount = \$MW->Frame()->pack(-expand
+ => '1', -fill => 'both',-side => 'top');";
                }
        elsif ($word eq "entry")
                {if ($debug) {print "Entry-";}
                $count++;
                $things=nextword;
                $default=nextword;
                eval "\$answer$count='$default';";
                if ($things eq "fill") {eval "\$textframe$fcount->Entr
+y(-textvariable=> \\\$answer$count, -width => 1, -relief => 
                         'sunken')->pack(-side => 'left', -expand => '
+1', -fill => 'x');";}
                else    {eval "\$textframe$fcount->Entry(-textvariable
+=> \\\$answer$count, -width => $things, -relief => 'sunken')->
                        pack(-side => 'left');";}
                }
        elsif ($word eq "label")
                {if ($debug) {print "Label-";}
                $things=nextword;
                eval "\$textframe$fcount->Label(-text => \$things)->pa
+ck(-side => 'left');";
                }
        elsif ($word eq "file")
                {if ($debug) {print "File-";}
                $default=nextword;
                @content=`cat $default`;
                foreach $things (@content) {chomp($things);unshift(@to
+do,$things);if ($things eq "crlf") {$frames++;};};
                return;
                }
        else    {print "Ignoring strange word $word\n";}
  }     }

$frames=1;
my $MW = MainWindow->new; $MW->title("FormBox");
$MW->Label(-text => "Version 1.1 - Fred Cohen")->pack(-side => 'bottom
+');
eval "\$textframe$fcount = \$MW->Frame()->pack(-expand => '1', -fill =
+> 'both',-side => 'top');";
                
while (scalar(@ARGV) > 0) {$nextitem=$ARGV[0];unshift(@todo, $nextitem
+);shift; if ($nextitem eq "crlf") {$frames++;};}
                 
if ($debug) {print "building $frames frames\n";}

for ($fdone=0;$fdone<=$frames;$fdone++) {eval "\$textframe$fdone = 
        \$MW->Frame()->pack(-expand => '1', -fill => 'both',-side => '
+top');";}

if ($debug) {for $x (@todo) {print "$x:";};}
while (scalar(@todo) > 0)
        {for (;$fdone<=$frames;$fdone++) {eval "\$textframe$fdone =
                \$MW->Frame()->pack(-expand => '1', -fill => 'both',-s
+ide => 'top');";}
        godoit();}

MainLoop;
Replies are listed 'Best First'.
Re: formbox.pl
by zentara (Cardinal) on Mar 18, 2004 at 15:48 UTC
    I get an error in this code block, a cut'n'paste error?
    elsif ($word eq "radio") {if ($debug) {print "Radio-";} $count++; eval "\$answer$count=\"\";"; $things=nextword; @things=split(/\s/, $things); foreach $item (@things) foreach $item (@things) {eval "\$textframe$fcount->Radiobutton(-text = +> '$item', -variable => \\\$answer$count, -value => '$item') ->pack(-side => 'left', -fill => 'x'); +";}
    You have a duplicate foreach $item (@things)

    I'm not really a human, but I play one on earth. flash japh
Re: formbox.pl
by graff (Chancellor) on Mar 19, 2004 at 04:02 UTC
    Whew! Where to begin? It is a kinda neat idea -- specify the layout of of the GUI on the command line; or, really nice: save it in a simple text file. Very appealing. I took out the one bad line that caused the syntax error, and it worked. But as for the implementation...
    • The thing is totally unproductive (i.e. no use) if the command line does not include an "OK" somewhere -- you should just put this at the bottom of the window if the user forgets to supply it.
    • Why only accept/print a single set of entries and then quit? Allow for a distinction between "Print" and "Exit", in case the user wants to go through a bunch of record entries in a single run (but then you have to worry about making sure the output is parsable -- most likely it will be redirected to a file or downstream process, and you might want something like XML to delimit the records). Maybe include a "Quit" case, too, to allow for skipping the output.
    • There appears to be a lot of "cut and paste" style programming here -- you make something work for one case, then just copy it and tweak it for every other case. And you don't notice the things that all these cases have in common, which should be done just once for all cases.
    • Did you every think about using an array or a hash to store widget handles and data? You can do that, and it would really help to cut down on the bulk, befuddlement and brittleness of your code. For every "eval ..." you have like these:
      eval "\$answer$count=0;"; eval "\$textframe$fcount->Checkbutton(...);";
      you could have something like the following, which will be much safer, saner, and easier to grasp and maintain:
      $answer[$count] = 0; $textframe[$fcount]->Checkbutton(...);
      Once you start to organize your widgets and related data as parts of an overall data structure, the code will be a lot more compact and a lot easier to enhance.

    I won't spend the time to try to prove this, but I suspect the same functionality could be achieved with about half as much code, or more likely, a third.

    update: Forgot to mention -- Please fix your indenting style. Try this command-line, putting the file name of your script as indicated:

    perl -MO=Deparse your_script.pl
    Doesn't that seem easier to read than what you posted? It does make a difference. (The output will differ in more than just the spacing, but the main point is to note the overall approach and layout.) Actually having tried it myself just now, it seems you have lots of unnecessary white space within your eval strings -- once you get rid of all those evals, it'll all be nicer in many respects.