Calculator - Step 4. Improve on User Interface

Objective for this step

Once we have the core business logic, and foolproofed the application, we can start improving the user interface (UI), such as making it nicer, or giving a better user experience.

Here are a few examples:

  • Change this to a 4 or 8 line display so that user can see clearly what has been entered earlier
  • Store the results in a history and allowing user to refer back or use any result in the history
  • Allow user to change past entry, e.g. the user entered 123+457, press the '=' sign, and realized that it should be 123+456. The user can press a button, go back to edit 457 to 456, and then press the '=' sign.

As mentioned earlier, the focus here is php-gtk2, and not the mechanics of the application itself. So we will only add a few cosmetic changes to the calculator here.

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   
69   
70   
71   
72   
73   
74   
75   
76   
77   
79   
80   
81   
82   
83   
84   
85   
86   
87   
88   
89   
90   
91   
92   
95   
96   
97   
98   
103   
104   
105   
106   
107   
108   
109   
110   
112   
113   
114   
115   
116   
117   
118   
119   
120   
121   
122   
123   
124   
125   
126   
128   
129   
130   
131   
132   
133   
134   
135   
136   
137   
138   
139   
140   
141   
142   
143   
144   
145   
146   
147   
148   
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   
187   
188   
189   
190   
191   
192   
193   
194   
195   
<?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
                $value = $button_label[$j][$i];
                $button = new GtkButton($value);
                $button->set_size_request(40, 32); // set the size
                $button->connect('clicked', array(&$this, "on_button"));
                if (preg_match("/^([0-9]|\.)$/", $value)) // digits
                    $button->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse('#FFFF33'));
                if (preg_match("/^(\+|-|\*|\/){1}$/", $value)) // +-*/
                    $button->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse('#ff99ff'));
                if ($value=='C' || $value=='CE') // Clear
                    $button->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse('#FF3366'));
                if ($value=='=') // =
                    $button->modify_bg(Gtk::STATE_NORMAL, GdkColor::parse('#0099FF'));
                $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;
                } 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)))
                    if ($value!='=')
                        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!
?>
 

Sample Output

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?