Calculator - Step 3. Validation Checks

Objective for this step

In this step, we will try to "foolproof" our calculator by putting in some validation checks .

The Code

Note: If you have installed php-gtk2 using Gnope Installer on Windows, and if running the sample code below gives you warning that the Symbolic names for keys (e.g. Gdk::KEY_Return) is not defined, you might want to update your php-gtk2 with the latest php-gtk2.dll available here. Simply download the php-gtk2.dll and replace the copy in the folder php-gtk2\ext. The latest compilation has put in the Symbolic names for keys listed here.

1   
2   
3   
4   
5   
6   
7   
8   
9   
10   
11   
12   
13   
14   
15   
16   
17   
18   
19   
20   
21   
22   
24   
25   
26   
27   
28   
29   
30   
31   
32   
33   
37   
38   
39   
45   
49   
50   
51   
52   
53   
54   
55   
56   
57   
58   
59   
60   
61   
62   
63   
64   
65   
66   
67   
68   
70   
71   
72   
73   
74   
75   
76   
77   
78   
79   
80   
81   
82   
83   
86   
87   
88   
89   
94   
95   
96   
97   
98   
99   
100   
101   
103   
104   
105   
106   
107   
108   
109   
110   
111   
112   
113   
114   
115   
116   
117   
119   
120   
121   
122   
123   
124   
125   
126   
127   
128   
129   
130   
131   
132   
133   
134   
135   
136   
137   
138   
139   
141   
142   
143   
144   
145   
146   
147   
148   
149   
150   
151   
152   
153   
154   
155   
156   
157   
158   
159   
160   
161   
162   
163   
164   
165   
166   
167   
168   
169   
170   
171   
172   
173   
174   
175   
176   
177   
178   
179   
180   
181   
182   
183   
184   
185   
186   
<?php

class Calculator{

    var $operator = '';
    var $stack = array();
    var $is_entering_number = 0;

    // constructor
    function Calculator() {
    }

    function main() {
        $this->setup_window();
        $this->setup_layout();
        $this->window->show_all(); // display the calculator
        Gtk::main(); // and let's go!
    }

    function setup_window() {
        $this->window = new GtkWindow();
        $this->window->set_size_request(200, 240);
        $this->window->connect_simple('destroy', array('Gtk','main_quit'));
        $this->window->connect('key-press-event', array(&$this, "on_keypress"));
    }

    function setup_layout() {
        // setup a vbox to hold display and buttons
        $vbox = new GtkVBox();
        $this->window->add($vbox);

        // setup display
        $this->display = new GtkEntry();
        $this->display->set_alignment(1.0); // right-justified
        $this->display->set_editable(false); // for display only
        $vbox->pack_start($this->display);

        // the button labels
        $button_label = array(
        array('BackSpc', 'CE', 'C'),
        array('7', '8', '9', '/', 'sqrt'),
        array('4', '5', '6', '*', '%'),
        array('1', '2', '3', '-', '1/x'),
        array('0', '+/-', '.', '+', '=')
        );

        // setup the buttons
        for ($j=0; $j<5; ++$j) {
            $hbox = new GtkHBox();
            $vbox->pack_start($hbox); // use a hbox to hold each row of buttons
            for ($i=0; $i<5; ++$i) {
                if ($j==0 && $i>2) continue; // first row contains only 3 buttons
                $button = new GtkButton($button_label[$j][$i]);
                $button->set_size_request(40, 32); // set the size
                $button->connect('clicked', array(&$this, "on_button"));
                $hbox->pack_start($button);
            }
        }
    }

    // process button click
    function on_button($button) {
        $value = $button->child->get_text();
        echo "button_click: $value\n";
        $this->process_input($value);
    }

    // process keyboard input
    function on_keypress($widget, $event) {
        $keyval = $event->keyval;
        $value = chr($keyval); // get the ASCII equivalent
        if (preg_match("/[0-9\+\-\*\/\.=]/", $value) || $keyval==Gdk::KEY_Return || $keyval==Gdk::KEY_BackSpace) {
            if ($keyval==Gdk::KEY_Return) $value='=';
            if ($keyval==Gdk::KEY_BackSpace) $value='BackSpc';
            $this->process_input($value);
            $this->display->set_position(-1); // move the cursor to the end of display
        }
    }

    // process input
    function process_input($value) {
        if (preg_match("/^([0-9]|\.)$/", $value)) {
            $number = array_pop($this->stack);
            if (preg_match("/^([0-9]|\.)+$/", $number)) {
                if ($value=='.') {
                    if (strpos($number,'.')===false) $number .= $value; // note 1
                } else {
                    $number .= $value;
                }
            } else {
                if ($number!='') array_push($this->stack, $number);
                $number = $value;
            }
            array_push($this->stack, $number);
            $this->display->set_text($number);

        } elseif (preg_match("/^(\+|-|\*|\/|=){1}$/", $value)) { // perform binary operations
            if (count($this->stack)<3) {
                if (preg_match("/^([0-9]|\.)+$/", end($this->stack))) // note 2
                    if ($value!='=') // note 3
                        array_push($this->stack, $value);
            } else {
                $number2 = array_pop($this->stack);
                $operator = array_pop($this->stack);
                $number1 = array_pop($this->stack);
                switch ($operator) {
                    case '+': $result = $number1 + $number2; break;
                    case '-': $result = $number1 - $number2; break;
                    case '*': $result = $number1 * $number2; break;
                    case '/': $result = $number1 / $number2; break;
                }
                $this->display->set_text($result);
                array_push($this->stack, $result);
                if ($value!='=') array_push($this->stack, $value);
            }

        //process unary operators
        } elseif ($value=='1/x' || $value=='sqrt' || $value=='%' || $value=='+/-') {
            if (count($this->stack)>=1) {
                $number = array_pop($this->stack);
                switch ($value) {
                    case '1/x': $result = 1/$number; break;
                    case 'sqrt': $result = sqrt($number); break;
                    case '%': $result = $number / 100; break;
                    case '+/-': $result = -$number; break;
                }
                $this->display->set_text($result);
                array_push($this->stack, $result);
            }

        } elseif ($value=='C') {
            $this->stack=array();
            $this->display->set_text('0');

        } elseif ($value=='CE') {
            if (preg_match("/^([0-9]|\.)+$/", end($this->stack))) {
                $number = array_pop($this->stack);
                $this->display->set_text('0');
            }

        } elseif ($value=='BackSpc') {
            if (preg_match("/^([0-9]|\.)+$/", end($this->stack))) {
                $number = array_pop($this->stack);
                $number = substr($number, 0, strlen($number)-1);
                $this->display->set_text($number);
                array_push($this->stack, $number);
            }
        }
        $this->show_stack(); // show current stack
    }

    // prints the current stack content
    function show_stack() {
        echo "current stack content:\n";
        for ($i=count($this->stack)-1; $i>=0; --$i) {
            echo "stack[$i] = {$this->stack[$i]}\n";
        }
        echo "\n";
    }
}

$cal = new Calculator(); // cerate a new calculator
$cal->main(); // let's run it!
?>
 

Explanation

Below are some of the validation checks that we have put in:

  1. don't allow two decimal points e.g. 1.2.3
  2. + - * / - will only be added to stack if top of stack is a number
  3. if there is only one number on stack, and the user press '=', the '=' should be go to stack.

There are many more other validation checks that need to put in place if you really want to make this calculator "foolproof". We won't go into all since the focus here is php-gtk2 as oppose to calculator.

Links

Search This Site

Google
Web This Site

Search PHP-GTK2 Manual

Full-text search on php-gtk2 manual

Members Login

Username:
Password:
Key:
What is this?
  Forget Password?