App-ErrorCalculator

 view release on metacpan or  search on metacpan

lib/App/ErrorCalculator.pm  view on Meta::CPAN

# titlebar), we ask it to call the delete_event () functio
# as defined above. No data is passed to the callback function.
$window->signal_connect(delete_event => \&_delete_event);

# Here we connect the "destroy" event to a signal handler.
# This event occurs when we call Gtk2::Widget::destroy on the window,
# or if we return FALSE in the "delete_event" callback. Perl supports
# anonymous subs, so we can use one of them for one line callbacks.
$window->signal_connect(destroy => sub { Gtk2->main_quit; });

my $table = Gtk2::Table->new(5, 4, FALSE);
$window->add($table);

# Labels
my $l = Gtk2::Label->new('Function:');
$table->attach_defaults(
	$l, 0, 1, # left/right
	1, 2, # top/bottom
);
$l->show;
$l = Gtk2::Label->new('Input:');
$table->attach_defaults(
	$l, 0, 1, # left/right
	2, 3, # top/bottom
);
$l->show;
$l = Gtk2::Label->new('Output:');
$table->attach_defaults(
	$l,	0, 1, # left/right
	3, 4, # top/bottom
);
$l->show;

# feedback labels
my $funclabel = Gtk2::Label->new('Valid Function  ');
$table->attach_defaults(
	$funclabel, 3, 4, # left/right
	1, 2, # top/bottom
);
$funclabel->show;
my $inlabel = Gtk2::Label->new('Invalid Data File');
$table->attach_defaults(
	$inlabel, 3, 4, # left/right
	2, 3, # top/bottom
);
$inlabel->show;
my $outlabel = Gtk2::Label->new('Invalid Output File');
$table->attach_defaults(
	$outlabel, 3, 4, # left/right
	3, 4, # top/bottom
);
$outlabel->show;

# Entries
my $funcentry = Gtk2::Entry->new;
$table->attach_defaults(
	$funcentry,	1, 2, # left/right
	1, 2, # top/bottom
);
$funcentry->signal_connect(
	activate => \&_validate_func,
);
$funcentry->signal_connect(
	changed  => \&_validate_func,
);
$funcentry->set_text('f = a * x^2');
$funcentry->show;

my $inentry = Gtk2::Entry->new;
$table->attach_defaults(
	$inentry,	1, 2, # left/right
	2, 3, # top/bottom
);
$inentry->signal_connect(
	activate => \&_read_file,
);
$inentry->show;

my $outentry = Gtk2::Entry->new;
$table->attach_defaults(
	$outentry,	1, 2, # left/right
	3, 4, # top/bottom
);
$outentry->show;

# buttons
my $valbutton = Gtk2::Button->new('Validate');
$table->attach_defaults(
	$valbutton,	2, 3, # left/right
	1, 2, # top/bottom
);
$valbutton->signal_connect(	clicked => \&_validate_func );
$valbutton->show;

my $inbutton = Gtk2::Button->new('Select File');
$table->attach_defaults(
	$inbutton,	2, 3, # left/right
	2, 3, # top/bottom
);
$inbutton->signal_connect(
	clicked => sub {
		_run_fileselection(
			'Select input file', $inentry,
			sub {
				_read_file();
			},
		);
	},
);
$inbutton->show;

my $outbutton = Gtk2::Button->new('Select File');
$table->attach_defaults(
	$outbutton,	2, 3, # left/right
	3, 4, # top/bottom
);
$outbutton->signal_connect(
	clicked => sub {
		my $t = $outentry->get_text;
		_run_fileselection(
			'Select output file', $outentry,
			sub {
				my $text = shift;
				if ( -e $text ) {
					my $r = ask Gtk2::Ex::Dialogs::Question( "File exists. Overwrite?" );
					$outentry->set_text($t), return if not $r;
					$outlabel->set_text('Valid Output File  ');
				}
				else {
					$outlabel->set_text('Valid Output File  ');
				}
			}
		);
   	},
);
$outbutton->show;

my $runbutton = Gtk2::Button->new('Run Calculation');
$table->attach_defaults(
	$runbutton,	0, 4, # left/right
	4, 5, # top/bottom
);
$runbutton->signal_connect(	clicked => \&_run_calculation );
$runbutton->show;

$table->set_col_spacings(10);
$table->set_row_spacings(10);

$table->show;

sub run {
	$window->show;
	Gtk2->main;
}

sub _run_fileselection {
	my $title = shift;
	my $entry = shift;
	my $callback = shift;
	my $fsel = Gtk2::FileSelection->new($title);
	$fsel->set_filename($entry->get_text);
	$fsel->ok_button->signal_connect(
		"clicked",
		sub {
			$entry->set_text($fsel->get_filename);
			$callback->($fsel->get_filename) if defined $callback;
			$fsel->destroy
		},
		$fsel
	);
	$fsel->cancel_button->signal_connect(
		"clicked",
		sub { $fsel->destroy },
		$fsel
	);
	$fsel->show;

}

sub _parse_function {
	my $f = shift;
	my ($name, $body) = split /\s*=\s*/, $f, 2;
	return() if (not defined $name or $name =~ /^\s*$/ or not defined $body);
	my $nobj;
	eval { $nobj = Math::Symbolic::Variable->new($name) };
	return() if not defined $nobj or not defined $nobj->name or $@;
	my $func;
	eval { $func = $Math::Symbolic::Parser->parse($body) };
	return() if not defined $func or $@;
	my $var = $nobj->name;
	# function must not be recursive
	return() if grep {$var eq $_} $func->signature;
	$func = $func->apply_derivatives()->simplify();
	return($nobj, $func);
}

my ($name, $body);
sub _validate_func {
	my $f = $funcentry->get_text;
	($name, $body) = _parse_function($f);
	if (not defined $name) {
		$funclabel->set_text('Invalid Function');
	}
	else {
		$funclabel->set_text('Valid Function  ');
	}
	
}

my $data;
sub _read_file {
	my $file = $inentry->get_text();
	if (not -e $file) {
		$inlabel->set_text('Invalid Data File');
		$data = undef;
	}
	my $ref = Spreadsheet::Read::ReadData($file);
	if (not defined $ref) {
		$inlabel->set_text('Invalid Data File');
		$data = undef;
	}
	else {
		$inlabel->set_text('Valid Data File  ');
		$data = $ref;
	}
}

sub _run_calculation {
	my $func = $body;

	if (not $funclabel->get_text() eq 'Valid Function  ') {
		new_and_run
		Gtk2::Ex::Dialogs::ErrorMsg( text => "You should give me a valid formula first." );	
		return();
	}
	
	if (not $inlabel->get_text() eq 'Valid Data File  ') {
		new_and_run
		Gtk2::Ex::Dialogs::ErrorMsg( text => "You need to select a valid input data file first." );	
		return();
	}
	
	if (not $outlabel->get_text() eq 'Valid Output File  ') {
		new_and_run
		Gtk2::Ex::Dialogs::ErrorMsg( text => "You need to select a valid output data file first." );	
		return();
	}
	
	my $sym = $name->name;
	my $csv = $data->[1];
	my $cell = $csv->{cell};
	my @vars = $func->signature;
	my %vars = map {($_ => undef)} @vars;

	my %errors;
	
	foreach my $col (1..$csv->{maxcol}) {
		my $name = $cell->[$col][1];



( run in 1.248 second using v1.01-cache-2.11-cpan-140bd7fdf52 )