14. Menu

Making menus can be an complex activity. For now, I made a simple statistics application, demonstrating only the basic features of a menu. More complex menu examples will follow in later chapters.

Notice that this program has statistical functions without using modules. Compare: 4. Practical example in Part 3

mainwindow


use Prima qw(Application Themes InputLine Buttons Label);
use Prima::Menus;

require "/home/reinier/mcMessage.pl"; # code 7. Messagewindow

$mw = Prima::MainWindow-> create(
    text => "Menu example",
    size => [ 600, 200],
    icon => Prima::Icon-> load('icon.png'),	
    backColor => 0x606060,	
    skin => 'flat',
    menuItems => [
        [ '~File' => [
                        [ '-', 'Preferences disabled', '', '', sub {} ], # disbabled by - prefix
                        [], # divisor line
                        [ 'Exit', 'Alt+X', '@X', sub { exit } ], # @X is equivalent to km::Alt | X
                     ]
        ],
        [ '~Insert' => [
                             [ '2-6 integers', 'Ctrl+1', '^1', sub { insert_random(1) } ], # ^1 is short for km::Ctrl | ord('1')
                             [ '7-11 integers', 'Ctrl+2', '^2', sub { insert_random(2) } ],
                           ]
        ],          
        [ '~Statistics' => [
                             [ 'Min', 'Alt+1', km::Alt | ord('1'), sub { min() } ], 
                             [ 'Max', 'Alt+2', km::Alt | ord('2'), sub { max() } ],     
                             [ 'Range', 'Alt+3', km::Alt | ord('3'), sub { range() } ],         
                             [ 'Sum', 'Alt+4', km::Alt | ord('4'), sub { sum() } ],        
                             [ 'Mean', 'Alt+5', km::Alt | ord('5'), sub { mean() } ],
                             [ 'Median', 'Alt+6', km::Alt | ord('6'), sub { median() } ],     
                             [ 'Standard deviation', 'Alt+7', km::Alt | ord('7'), sub { standard_deviation() } ],                               
                                                                                                           
                           ]
        ],
      
        [], # divisor in main menu: menuitem Help is now at the right
        [ '~Help' => [
                       [ 'Short cuts' => sub { mcMessage(300, 300, "Short cuts", "Insert integers\n2-6 integers: Ctrl+1\n7-11 integers: Ctrl+2\n\nStatistics\nMin: Alt+1\nMax: Alt+2\nRange: Alt+3\nSum: Alt+4\nAverage: Alt+5\nMedian: Alt+6\nStandard deviation: Alt+7"); } ],
                       [], # divisor line
                       [ 'About' => sub { mcMessage(300, 125, "About", "This is a demo application about menus\nin Prima - a perl graphic toolkit");}],                       
                       
                     ]
        ],
     ],

 );
 
$mw-> insert( "Label",
	pack      => { pad => 20, fill => 'none' },
	text   => "Enter integers, separated by a space (or use Insert) and select your Statistics formula:",
	valignment => ta::Center,
); 
 
$in = $mw-> insert( "InputLine",
    pack => { pady => 20, padx => 20, fill => 'both'}, },
    text => "",
    valignment => ta::Center,
    size => [500, 30],
    autoHeight => 1,
        # blink signals invalid input by setting background to red color; set for your own color the property backColor in blink, e.g. blink(backColor=>0xFF00FF)
    onChange  => sub { $out-> set(text => ""); $in-> blink if($in-> text !~ (/^[0-9 ]+$/));}
    popupItems     => [], # for now, we disable the right mouseclick
);

$out = $mw-> insert( "Label",
    pack      => { pad => 20, fill => 'none' },
    text => "",
    font   => { size => 14, },	
    valignment => ta::Center,
); 

sub insert_random {
	
    $category = shift;
    if ($category == 1) {
	
      $number_of_integers = 2 + int(rand((6 + 1) - 2)); # 2-6 random numbers	
    }	
    else {
		
      $number_of_integers = 7 + int(rand((11 + 1) - 7)); # 7-11 random numbers
    }	

    $integer_string = "";
    for ($i = 0; $i < $number_of_integers; $i++) {
	
       $integer_string = $integer_string . int(rand(100)) . " "; # integers between 0 - 100 (exclusive)	
    }
    
    chop($integer_string);
    $in-> set(text => $integer_string);
    
    $in-> autoSelect(0); # no text is selected  
    $in -> charOffset(length($integer_string)); # cursor at the end
}	

sub validate {
	
    $data = shift;
    $data =~ s/\h+/ /g;
    if($data =~ (/^[0-9 ]+$/) and (length($data) > 2) ) { # minimal two integers and a space, i.e. 1 2
      return $data;
    }
    else {
      $in -> blink;	
      return 0;
    }
	
}

sub min {
	
   if ($data = validate($in->text)) {

      @numbers = sort { $a <=> $b } split(" ", $data);
      $out-> set(text => "Lowest number: " . $numbers[0]);
      
   }
}

sub max {
	
   if ($data = validate($in->text)) {

      @numbers = sort { $a <=> $b } split(" ", $data);
      $out-> set(text => "Highest number: " . $numbers[$#numbers]);
      
   }
}

sub range {
	
   if ($data = validate($in->text)) {

      @numbers = sort { $a <=> $b } split(" ", $data);
      $out-> set(text => "Range: " . ($numbers[$#numbers] - $numbers[0]));
      
   }
}

sub sum {
	
  if ($data = validate($in->text)) {
	  
	  $sum = 0;
 	  @numbers = split(" ", $data);
	  foreach (@numbers) {
		 $sum = $sum + $_;
      }
      
      $out-> set(text => "Sum: " . $sum);

  }

}
 
sub mean {
	
   if ($data = validate($in->text)) {

	  $sum = 0;

 	  @numbers = split(" ", $data);
	  foreach (@numbers) {
		 $sum = $sum + $_;
      }
      
      $mean = $sum/(scalar(@numbers));
      $out-> set(text => "mean: " . $mean);
      
   }
}	 

sub median {
	
   if ($data = validate($in->text)) {

      @numbers = sort { $a <=> $b } split(" ", $data);
      $len_array = (scalar(@numbers)); 
      if ($len_array % 2) {
        $median = $numbers[($len_array+1)/2 - 1];
      } else {
        $median = ($numbers[($len_array)/2 - 1] + $numbers[($len_array)/2])/2;		  
      }	  

      $out-> set(text => "Median: " . $median);
      
   }
}

sub standard_deviation {
	
   if ($data = validate($in->text)) {
	  
      # first: find mean 
      
      $sum = 0;

      @numbers = split(" ", $data);
      foreach (@numbers) {
        $sum = $sum + $_;
      }
      
      $mean_1 = $sum/(scalar(@numbers));
      
      # second: find second mean (variance): sum of squared differences between each number and the first mean, divided by the number of integers
      
      $sum = 0;      

      foreach (@numbers) {
         $sum = $sum + (($_ - $mean_1)**2);
      } 
      $mean_2 = $sum/((scalar(@numbers)) - 1);# minus 1 -> formula for sample (not a population)
      
      # third: the standard deviation is the square root of the second mean
      
      $standard_dev = sprintf("%.2f", sqrt($mean_2)); # format the resultto 2 decimal places
      
      $out-> set(text => "Standard deviation: " . $standard_dev);     

   }
}

 
$in->focused(1); # cursor shown in inputline

run Prima;