#!/usr/bin/env perl -w require Tk; use Tk; use Tk::Font; use Tk::Text; use Tk::ROText; use English; use strict; use warnings; our %density = ( DI => "1.0", HF => "1.16", NH4OH => "0.9", H202 => "1.11", HCl => "1.18", H2SO4 => "1.84", Citric => "1", NH4F => "1.01", TMAH => "1.0", Nitric => "1.415", Acetic => "1.05", Phosphoric => "1.7", IPA => "0.79" ); our %concentration =( DI => "1.0", HF => "0.49", NH4OH => "0.29", H202 => "0.3", HCl => "0.37", H2SO4 => ".98", Citric => "0.5", NH4F => "0.4", TMAH => "0.25", Nitric => "0.7", Acetic => "0.8", Phosphoric => "0.85", IPA => "1.0" ); my $mw = MainWindow->new; $mw -> title("BathCalc Selection"); $mw -> geometry("+200+200"); #Setting frames in main window. my $menuframe = $mw -> Frame( -relief => "groove", -borderwidth => 2 )->pack( -side => "top", -fill => "x", ); my $topframe = $mw -> Frame()->pack( -side => "top", -fill => "x", -padx => 10, -pady => 5 ); my $midframe = $mw -> Frame()->pack( -side => "top", -fill => "x", -padx => 10, ); my $botframe = $mw -> Frame()->pack( -side => "top", -fill => "x", -padx => 10, -pady => 5 ); #command to make a file menubutton my $file_mw = $menuframe -> Menubutton ( -text => "File", -activeforeground => "grey" ) -> pack (-side => 'left'); $file_mw -> command( -label => "Exit", -command => sub{$mw -> destroy} ); my $help_mw = $menuframe -> Menubutton ( -text => "Help", -activeforeground => "grey" ) -> pack (-side => 'right'); $help_mw -> command( -label => "About", -command => sub{&ABOUT}); #setting up selections as to where to go next my $rb; my $r1=$midframe->Radiobutton( -text=>"Calculate Amount of Material to Pour a Bath", -value=>"1", -variable=>\$rb ) -> pack(-side => 'top', -anchor => 'w'); my $r2=$midframe->Radiobutton( -text=>"Convert Chemical Ratio to Mass Percent", -value=>"2", -variable=>\$rb ) -> pack(-side => 'top', -anchor => 'w'); my $r3=$midframe->Radiobutton( -text=>"Display Assumptions", -value=>"3", -variable=>\$rb ) -> pack(-side => 'top', -anchor => 'w'); my $text1 = $topframe->Label ( -text => "Selection:", -font => '{times new roman} 14' ) -> pack(-side => 'top', -anchor => 'w'); my $actionButton = $botframe -> Button( -text => "Go", -command => \&CHOOSE ) -> pack(-side => 'left', -padx => '50', -pady => '20'); my $exitButton = $botframe -> Button( -text => "Quit", -command => sub{exit} ) -> pack(-side => 'right', -padx => '50', -pady => '20'); MainLoop (); sub CHOOSE { if ($rb == 1) { &MAKE_BATH; } elsif ($rb == 2) { &RATIO; } elsif ($rb == 3) { &ASSUME; } } ############################################################################ ############################################################################ # This creates the window for the make bath screen # # This subroutine will generate a new window that will require user input to calculate volumes # density will be displayed and allow users to change if they need to do so. # required to pour up a bath of known size and concentration. The default concentration and # # # MAIN -> MB ########################################################################### sub MAKE_BATH { my $mb = MainWindow -> new; $mb -> title("Bath Pour Up"); $mb -> geometry("+200+200"); #this frame is going to be the menu bar. my $frame1 = $mb -> Frame( -relief => "groove", -borderwidth => 3 )->pack( -side => "top", -fill => "x" ); my $file_mb = $frame1 -> Menubutton ( -text => "File", -activeforeground => "grey" ) -> pack (-side => 'left'); $file_mb -> command( -label => "Exit", -command => sub{$mb -> destroy}); my $help_mb = $frame1 -> Menubutton ( -text => "Help", -activeforeground => "grey" ) -> pack (-side => 'right'); $help_mb -> command( -label => "Help", -command => sub{&MBHELP} ); $help_mb -> separator(); $help_mb -> command( -label => "About", -command => sub{&ABOUT} #The next section sets up all of the frames I will need, many, many frames... ); my $midhold = $mb -> Frame()->pack( -side => "top", -fill => "x", -padx => 3 ); my $frame2 = $midhold -> Frame()->pack( -side => "top", -anchor => "n" ); my $frame3 = $midhold -> Frame()->pack( -side => "top", -anchor => "n" ); my $calcVolframe = $mb -> Frame( -borderwidth => 3 )->pack( -side => "top", -fill => 'x', ); my $frame12 = $mb -> Frame( #this is the bottom frame with the status button in it. -relief => "raised", -borderwidth => 3 )->pack( -side => "bottom", -fill => 'x', ); our $totalvolume; my $voltext = $calcVolframe -> Entry( -textvariable => \$totalvolume, -width => 5 ) -> pack( -side => 'right', -padx => 36); my $volLbl = $calcVolframe ->Label( -text => "Bath Volume (L):" ) -> pack(-side=>'right'); #------------------------------------------------------------ #Calculate Button Below #------------------------------------------------------------ my $calcButton = $calcVolframe -> Button( -text => "Calculate", -command => \&VOLCALC ) -> pack(-side => 'left', -padx => '50', -pady => '10'); #------------------------------------------------------------ #Calculate Button Above #------------------------------------------------------------ our $i = 0; our(@wtpercent, @wtpercentbox, @concentration, $isSolv, @volume, @density, %chemname, @volumebox); my (@concentrationbox, @densitybox); #need to make sure that concentration and density are always in the same order. my $j = 0; foreach (sort keys(%concentration)){ $concentration[$j] = $concentration{$_}; $density[$j] = $density{$_}; $chemname{$_} = $j; $wtpercent[$j] = 0; $j++; }; my $headingOne = $frame3 ->Label( -text => "Solvent", -relief => "raised" ) -> grid (-column=>0, -row => 1); my $headingTwo = $frame3 ->Label( -relief => "raised", -text => "Chemical Name" ) -> grid (-column=>1, -row => 1); my $headingThree = $frame3 ->Label( -relief => "raised", -text => "Density (g/mL)" ) -> grid (-column=>2, -row => 1); my $headingFour = $frame3 ->Label( -text => "Concentration (%)", -relief => "raised" ) -> grid (-column=>3, -row => 1); my $headingFive = $frame3 ->Label( -text => "Wt %", -relief => "raised", -width => 5 ) -> grid (-column=>4, -row => 1); my $headingSix = $frame3 ->Label( -text => "Calculated Volume (L)", -relief => "raised" ) -> grid (-column=>5, -row => 1); for my $x (sort keys %density) { my $row = $i+2; $frame3->Radiobutton( -value=>$i, -variable => \$isSolv, -command=>[\&colorme, $i])->grid(-row=>$row,-column=>0); $frame3->Label(-text=>$x)->grid(-row=>$row,-column=>1); $densitybox[$i] = $frame3->Entry(-bg=>'grey', -width => 5, -textvariable => \$density[$i])->grid(-row=>$row,-column=>2); $concentrationbox[$i] = $frame3->Entry(-bg=>'grey', -width => 5, -textvariable => \$concentration[$i])->grid(-row=>$row,-column=>3); $wtpercentbox[$i] = $frame3->Entry(-bg=>'white', -width => 5, -textvariable => \$wtpercent[$i])->grid(-row=>$row,-column=>4); $volumebox[$i] = $frame3->Entry(-bg=>'grey', -width => 7, -textvariable => \$volume[$i])->grid(-row=>$row,-column=>5); $i++; } our $statustext = "Please enter desired weight percent and solvent."; my $statuslabel = $frame12 -> Label( -text => "Status: " ) -> pack(-side => "left"); my $status =$frame12 -> Entry( -textvariable => \$statustext, -foreground => 'red', -width => 60 ) -> pack(-side => "left"); } ############################################################################ ############################################################################ # This creates the window for the ratio screen # # MAIN -> RATIO ########################################################################### sub RATIO { my $rt = MainWindow -> new; $rt -> title("Ratio to Weight Percent"); $rt -> geometry("+200+200"); #this frame is going to be the menu bar. my $frame1 = $rt -> Frame( -relief => "groove", -borderwidth => 3 )->pack( -side => "top", -fill => "x" ); my $file_rt = $frame1 -> Menubutton ( -text => "File", -activeforeground => "grey" ) -> pack (-side => 'left'); $file_rt -> command( -label => "Exit", -command => sub{$rt -> destroy}); my $help_rt = $frame1 -> Menubutton ( -text => "Help", -activeforeground => "grey" ) -> pack (-side => 'right'); $help_rt -> command( -label => "Help", -command => sub{&RTHELP} ); $help_rt -> separator(); $help_rt -> command( -label => "About", -command => sub{&ABOUT} ); #The next section sets up all of the frames I will need, many, many frames... my $midhold = $rt -> Frame()->pack( -side => "top", -fill => "x", -padx => 3 ); my $frame2 = $midhold -> Frame()->pack( -side => "top", -anchor => "n" ); our $rtframe3 = $midhold -> Frame()->pack( -side => "top", -anchor => "n" ); our $calcVolframe = $rt -> Frame( -borderwidth => 3 )->pack( -side => "top", -fill => 'x', ); my $frame12 = $rt -> Frame( #this is the bottom frame with the status button in it. -relief => "raised", -borderwidth => 3 )->pack( -side => "bottom", -fill => 'x', ); our $VolButton = $calcVolframe -> Button( -text => "Bath Volume >>", -command => \&RATIOSUP ) -> pack(-side => 'right', -padx => '5', -pady => '5'); our $mOrv; my $mOrvbutton = $calcVolframe -> Radiobutton( -variable => \$mOrv, -value => "M", -width => 5 ) -> pack( -side => 'right'); my $massText = $calcVolframe -> Label( -text => "Mass Ratio ", -padx => 2) -> pack(-side => "right"); my $mOrvbutton2 = $calcVolframe -> Radiobutton( -variable => \$mOrv, -value => "V", -width => 5 ) -> pack( -side => 'right'); my $volLbl = $calcVolframe ->Label( -text => "Volume Ratio:" ) -> pack(-side=>'right'); #------------------------------------------------------------ #Calculate Button Below #------------------------------------------------------------ my $calcButton = $calcVolframe -> Button( -text => "Calculate", -command => \&RATIOCALC ) -> pack(-side => 'left', -padx => '50', -pady => '10'); #------------------------------------------------------------ #Calculate Button Above #------------------------------------------------------------ our(@ratio, @ratiobox, @concentration, @wtPercent, @density, %chemname, @wtpercentbox, @ratioVolbox, @ratioVol); my (@concentrationbox, @densitybox); #need to make sure that concentration and density are always in the same order. my $j = 0; foreach (sort keys(%concentration)){ $concentration[$j] = $concentration{$_}; $density[$j] = $density{$_}; $chemname{$_} = $j; $ratio[$j] = 0; $wtPercent[$j] = 0; $j++; }; my $headingTwo = $rtframe3 ->Label( -relief => "raised", -text => "Chemical Name" ) -> grid (-column=>1, -row => 1, -padx => 3, -pady =>2); my $headingThree = $rtframe3 ->Label( -relief => "raised", -text => "Density (g/mL)" ) -> grid (-column=>2, -row => 1, -padx => 3, -pady =>2); my $headingFour = $rtframe3 ->Label( -text => "Concentration", -relief => "raised" ) -> grid (-column=>3, -row => 1, -padx => 3, -pady =>2); my $headingFive = $rtframe3 ->Label( -text => "Ratio", -relief => "raised", -width => 5 ) -> grid (-column=>4, -row => 1, -padx => 3, -pady =>2); my $headingSix = $rtframe3 ->Label( -text => "Calculated Wt Percent", -relief => "raised" ) -> grid (-column=>5, -row => 1, -padx => 3, -pady =>2); my $i = 0; for my $chems (sort keys %density) { my $row = $i+2; $rtframe3->Label(-text=>$chems)->grid(-row=>$row,-column=>1); $densitybox[$i] = $rtframe3->Entry(-bg=>'grey', -width => 5, -textvariable => \$density[$i], -justify => "center")->grid(-row=>$row,-column=>2); $concentrationbox[$i] = $rtframe3->Entry(-bg=>'grey', -width => 5, -textvariable => \$concentration[$i], -justify => "center")->grid(-row=>$row,-column=>3); $ratiobox[$i] = $rtframe3->Entry(-bg=>'white', -width => 5, -textvariable => \$ratio[$i], -justify => "center")->grid(-row=>$row,-column=>4); $wtpercentbox[$i] = $rtframe3->Entry(-bg=>'grey', -textvariable => \$wtPercent[$i], -width => 5, -justify => "center")->grid(-row=>$row,-column=>5); $i++; } our $statustext = "Please enter desired ratio and whether wt or vol percent."; my $statuslabel = $frame12 -> Label( -text => "Status: " ) -> pack(-side => "left"); my $status =$frame12 -> Entry( -textvariable => \$statustext, -foreground => 'red', -width => 60 ) -> pack(-side => "left"); } ############################################################################ ############################################################################ # This sub is responsible for calculating the wt percents of the chemicals # supplied in the ratio screen # MAIN -> MB -> VOLCALC ########################################################################### sub VOLCALC { our(@wtpercent, @wtpercentbox, @concentration, @volume, @density, %chemname, @volumebox, $isSolv, $statustext, $totalvolume); my @densityfraction; if (defined($totalvolume)){ if (defined($isSolv)){ my @chemname = (sort keys(our %density)); my $solventname = $chemname[$isSolv]; $statustext = "Calculating"; my @concRatio; $wtpercent[$isSolv] = 0; for (my $i=0; $i <= $#chemname; $i++){ $concRatio[$i] = (.01 * $wtpercent[$i]) / $concentration[$i]; $densityfraction[$i] = $concRatio[$i] * $density[$i]; } my $percentsolvent = 1 - sum(@concRatio); my $densityfractionsolvent = $percentsolvent * $density[$isSolv]; $densityfraction[$isSolv] = $densityfractionsolvent; my $estdensity = sum(@densityfraction); my $totalmass = $totalvolume * $estdensity; my @componentmass = map{$_ * $totalmass}@concRatio; my $i; for ($i=0; $i <= $#componentmass; $i++){ $volume[$i] = $componentmass[$i] / $density[$i]; } my $volumewithoutsolvent = sum(@volume); $volume[$isSolv] = $totalvolume - $volumewithoutsolvent; $statustext = ("Calculation Complete"); my $l = 0; foreach (@volume){ $volume[$l] = sprintf("%.3f", $volume[$l]); $l++; }; foreach (@volume){ if ($_ < 0){ $statustext = "You screwed up somewhere!" } } } else { $statustext = "You need to select a solvent" } } else { $statustext = "You need to provide a bath volume"; } }; ############################################################################ ############################################################################ # This sub is responsible for calculating the wt percents of the chemicals # supplied in the ratio screen # MAIN -> RT -> RATIOCALC ########################################################################### sub RATIOCALC { our ($mOrv, @ratio, @density, @concentration, %chemname, @wtPercent, $statustext, @wtpercentbox); our (@volratio, $totalVol, @ratioVol); my (@wtPureAmt, @residualDI); if (defined($mOrv)){ $statustext = "Calculating..."; if ($mOrv eq "V"){ for (my $i = 0; $i <= $#ratio; $i++){ $ratio[$i] = $ratio[$i] * $density[$i]; $mOrv = "M"; }; }; for (my $j = 0; $j <= $#ratio; $j++){ $wtPureAmt[$j] = $ratio[$j] * $concentration[$j]; }; for (my $k = 0; $k <= $#ratio; $k++){ $residualDI[$k] = $ratio[$k] * (1 - $concentration[$k]); }; my $totalmass = sum(@ratio); my $diIndex = $chemname{'DI'}; if ($totalmass != 0){@wtPercent = map{$_ / $totalmass}@wtPureAmt}else{$statustext = "You need to give me numbers!"}; if ($wtPureAmt[$diIndex] == 0) { $wtPercent[$diIndex] = sum(@residualDI) / $totalmass; } else { $wtPercent[$diIndex] = (sum(@residualDI) + $wtPureAmt[$diIndex]) / $totalmass; } $statustext = "Calculation Complete"; }else{ $statustext = "You need to select weight or volume ratio!" }; my $l = 0; foreach (@wtPercent){ $wtPercent[$l] = sprintf("%.3f", $wtPercent[$l]); $wtPercent[$l] = $wtPercent[$l] * 100; $wtpercentbox[$l] -> configure( -textvariable => \$wtPercent[$l]); $l++; }; if ($totalVol != 0){ if (our $volEntryOpen == 1){ if ($mOrv eq "M"){ for (my $i = 0; $i <= $#ratio; $i++){ $volratio[$i] = $ratio[$i] / $density[$i]; }; }; my $j; for ($j=0; $j <= $#volratio; $j++){ $ratioVol[$j] = $totalVol * ($volratio[$j] / sum(@volratio)); $ratioVol[$j] = sprintf("%.3f", $ratioVol[$j]); }; } } else { $statustext = "You need to provide a total volume in liters." }; } ############################################################################ ############################################################################ # this subroutine sums the numbers passed to it. Returns a single value. # # ########################################################################### sub sum { my $sum = 0; foreach my $line (@_) { $sum += $line; } return $sum; } ############################################################################ ############################################################################ # This subroutine disables and colors the wtpercentbox of the selected # solvent in the Make Bath calculation window # MAIN -> MB -> COLORME ########################################################################### sub colorme { our @wtpercentbox; my $i = shift; map{$_->configure(-bg=>'white')}@wtpercentbox; $wtpercentbox[$i]->configure(-bg=>'black'); map{$_->configure(-state=>'normal')}@wtpercentbox; $wtpercentbox[$i]->configure(-state => 'disabled'); } ############################################################################ ############################################################################ # This window is created when the Bath Volume button is pushed in the # ratio window calculation screen. It will provide the volumes of chemical # to add to create a bath of known volume # RT -> RATIO -> RATIOSUP ########################################################################### sub RATIOSUP { our (@ratio, $rtframe3, @ratioVol, @ratioVolbox, $VolButton, $calcVolframe, $totalVol, $statustext, $mOrv, @density); my ($row, @volratio); my $headingSeven = $rtframe3 ->Label( -text => "Pour Up Volume (L)", -relief => "raised" ) -> grid (-column=>6, -row => 1, -padx => 3, -pady =>2); $VolButton -> packForget; $statustext = "You need to provide a total volume"; my $i = 0; foreach my $line (@ratio) { $row = $i+2; $ratioVolbox[$i] = $rtframe3->Entry(-bg=>'grey', -width => 7, -textvariable => \$ratioVol[$i], -justify => 'center')->grid(-row=>$row,-column=>6); $i++; } my $newrow = $row +1; our $totvolbox = $rtframe3 -> Entry(-bg => 'white', -width => 7, -textvariable => \$totalVol, -justify => 'center') -> grid (-row=>$newrow, -column => 6, -pady => 3); my $totvolLab = $rtframe3 -> Label(-text => "Total Volume (L)") -> grid (-row=>$newrow, -column => 5, -pady => 3); our $volEntryOpen = 1; } ############################################################################ ############################################################################ # This window is the about box for all windows # # MAIN -> ABOUT ########################################################################### sub ABOUT #The about box in the help menu! { my $aboutwin = MainWindow -> new; $aboutwin -> title("About"); $aboutwin -> geometry("+210+210"); my $aboutleftframe = $aboutwin -> Frame() -> pack(-side => "left"); my $aboutrightframe = $aboutwin -> Frame() -> pack(-side => "left"); my $exitButton = $aboutrightframe -> Button( -text => "Close", -command => sub{destroy $aboutwin} ) -> pack(-side => 'right', -padx => '10', -pady => '10'); my $words = qq( BathCalc ver.2.0 Written by David Daycock. August 2004 Send bugs to: david\@netboise.com); my $aboutText = $aboutleftframe->Scrolled('ROText', -height => '10', -width => '20', -wrap => "word", -scrollbars => 'osoe', ); $aboutText -> insert('end', $words); $aboutText -> pack(-side => "left", -padx => '10', -pady => '10'); } ############################################################################ ############################################################################ # This is the HELP MENU in the RATIO CALC window ########################################################################### sub RTHELP { my $rthelpwin = MainWindow -> new; $rthelpwin -> title("Help"); $rthelpwin -> geometry("+210+210"); my $rthelpleftframe = $rthelpwin -> Frame() -> pack(-side => "left"); my $rthelprightframe = $rthelpwin -> Frame() -> pack(-side => "left"); my $words = qq( This utility will help you calculate mass percentages and volumes to pour up baths. The concentration and the densities have been provided based on standard concentrated chemicals. These values are able to be modified by the user. Concentrations are provided in weight fraction, and density in grams per mL. When you click the "Make Bath" button, you will be prompted to enter a total bath volume in liters. ); my $rthelpText = $rthelpleftframe->Scrolled('ROText', -height => '25', -width => '100', -wrap => "word", -scrollbars => 'osoe', ); $rthelpText -> insert('end', $words); $rthelpText -> pack(-side => "left", -padx => '10', -pady => '10'); } ############################################################################ ############################################################################ # This is the HELP MENU in the MAKE BATH CALC window ########################################################################### sub MBHELP { my $mbhelpwin = MainWindow -> new; $mbhelpwin -> title("Help"); $mbhelpwin -> geometry("+210+210"); my $mbhelpframe = $mbhelpwin -> Frame() -> pack(-side => "left"); my $words = qq( This utility will calculate pour up volumes if you have desired final wt percent goal. The wt percent column should be entered as mass fraction * 100. (0.02 mass fraction of component A = 2 (wt %)); You will need to provide a total bath volume, then select the solvent. For instance, if I want to pour up 12L of 2% TMAH in DI, you would first type in 12 in the Bath Volume box, then click the DI radio button, then type "2" in wt % box in the TMAH row. Results are supplied in Liters rounded to the nearest mL. ); my $mbhelpText = $mbhelpframe->Scrolled('ROText', -height => '25', -width => '100', -wrap => "word", -scrollbars => 'osoe', ); $mbhelpText -> insert('end', $words); $mbhelpText -> pack(-side => "left", -padx => '10', -pady => '10'); } ############################################################################ ############################################################################ # This is the Assumptions window from the top main window ########################################################################### sub ASSUME { my $assumewin = MainWindow -> new; $assumewin -> title("Assumptions"); $assumewin -> geometry("+210+210"); my $assumeframe = $assumewin -> Frame() -> pack(-side => "left"); my $words = qq( The only assumption you can not modify is this: When calculating bath pour up based on desired weight pecent, the denisty of the total solution is assumed to be the mass percent weighted averages of the components' density. ); my $assumeText = $assumeframe->Scrolled('ROText', -height => '25', -width => '50', -wrap => "word", -scrollbars => 'osoe', ); $assumeText -> insert('end', $words); $assumeText -> pack(-side => "left", -padx => '10', -pady => '10'); }