~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to doc/html/widgets-calculator.html

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
Import upstream version 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?xml version="1.0" encoding="iso-8859-1"?>
 
2
<!DOCTYPE html
 
3
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
 
4
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 
5
<!-- /tmp/qt-4.0.0-espenr-1119621036935/qt-x11-opensource-desktop-4.0.0/doc/src/examples/calculator.qdoc -->
 
6
<head>
 
7
    <title>Qt 4.0: Calculator Example</title>
 
8
    <style>h3.fn,span.fn { margin-left: 1cm; text-indent: -1cm; }
 
9
a:link { color: #004faf; text-decoration: none }
 
10
a:visited { color: #672967; text-decoration: none }
 
11
td.postheader { font-family: sans-serif }
 
12
tr.address { font-family: sans-serif }
 
13
body { background: #ffffff; color: black; }</style>
 
14
</head>
 
15
<body>
 
16
<table border="0" cellpadding="0" cellspacing="0" width="100%">
 
17
<tr>
 
18
<td align="left" valign="top" width="32"><img src="images/qt-logo.png" align="left" width="32" height="32" border="0" /></td>
 
19
<td width="1">&nbsp;&nbsp;</td><td class="postheader" valign="center"><a href="index.html"><font color="#004faf">Home</font></a>&nbsp;&middot; <a href="classes.html"><font color="#004faf">All&nbsp;Classes</font></a>&nbsp;&middot; <a href="mainclasses.html"><font color="#004faf">Main&nbsp;Classes</font></a>&nbsp;&middot; <a href="annotated.html"><font color="#004faf">Annotated</font></a>&nbsp;&middot; <a href="groups.html"><font color="#004faf">Grouped&nbsp;Classes</font></a>&nbsp;&middot; <a href="functions.html"><font color="#004faf">Functions</font></a></td>
 
20
<td align="right" valign="top" width="230"><img src="images/trolltech-logo.png" align="right" width="203" height="32" border="0" /></td></tr></table><h1 align="center">Calculator Example</h1>
 
21
<p>Files:</p>
 
22
<ul>
 
23
<li><a href="widgets-calculator-button-cpp.html">widgets/calculator/button.cpp</a></li>
 
24
<li><a href="widgets-calculator-button-h.html">widgets/calculator/button.h</a></li>
 
25
<li><a href="widgets-calculator-calculator-cpp.html">widgets/calculator/calculator.cpp</a></li>
 
26
<li><a href="widgets-calculator-calculator-h.html">widgets/calculator/calculator.h</a></li>
 
27
<li><a href="widgets-calculator-main-cpp.html">widgets/calculator/main.cpp</a></li>
 
28
</ul>
 
29
<p>The example shows how to use signals and slots to implement the functionality of a calculator widget, and how to use <a href="qgridlayout.html">QGridLayout</a> to place child widgets in a grid. This example also demonstrates how to use <a href="qobject.html#installEventFilter">event filters</a> to customize a widget's behavior.</p>
 
30
<center><img src="images/calculator-example.png" alt="Screenshot of the Calculator example" /></center><p>The example consists of two classes:</p>
 
31
<ul>
 
32
<li><tt>Calculator</tt> is the calculator widget, with all the calculator functionality.</li>
 
33
<li><tt>Button</tt> is the widget used for each of the calculator button. It derives from <a href="qtoolbutton.html">QToolButton</a>.</li>
 
34
</ul>
 
35
<p>We will start by reviewing <tt>Calculator</tt>, then we will take a look at <tt>Button</tt>.</p>
 
36
<a name="calculator-class-definition"></a>
 
37
<h2>Calculator Class Definition</h2>
 
38
<pre>&nbsp;   class Calculator : public QDialog
 
39
    {
 
40
        Q_OBJECT
 
41
 
 
42
    public:
 
43
        Calculator(QWidget *parent = 0);
 
44
 
 
45
    protected:
 
46
        bool eventFilter(QObject *target, QEvent *event);
 
47
 
 
48
    private slots:
 
49
        void digitClicked();
 
50
        void unaryOperatorClicked();
 
51
        void additiveOperatorClicked();
 
52
        void multiplicativeOperatorClicked();
 
53
        void equalClicked();
 
54
        void pointClicked();
 
55
        void changeSignClicked();
 
56
        void backspaceClicked();
 
57
        void clear();
 
58
        void clearAll();
 
59
        void clearMemory();
 
60
        void readMemory();
 
61
        void setMemory();
 
62
        void addToMemory();</pre>
 
63
<p>The <tt>Calculator</tt> class provides a simple calculator widget. It inherits from <a href="qdialog.html">QDialog</a> and has several private slots associated with the calculator's buttons. <a href="qobject.html#eventFilter">QObject::eventFilter</a>() is reimplemented to handle mouse events on the calculator's display.</p>
 
64
<p>Buttons are grouped in categories according to their behavior. For example, all the digit buttons (labeled <b>0</b> to <b>9</b>) append a digit to the current operand. For these, we connect multiple buttons to the same slot (e.g., <tt>digitClicked()</tt>). The categories are digits, unary operators (<b>Sqrt</b>, <b>x�</b>, <b>1/x</b>), additive operators (<b>+</b>, <b>-</b>), and multiplicative operators (<b>�</b>, <b>�</b>). The other buttons have their own slots.</p>
 
65
<pre>&nbsp;   private:
 
66
        Button *createButton(const QString &amp;text, const QColor &amp;color,
 
67
                             const char *member);
 
68
        void abortOperation();
 
69
        bool calculate(double rightOperand, const QString &amp;pendingOperator);</pre>
 
70
<p>The private <tt>createButton()</tt> function is used as part of the widget construction. <tt>abortOperation()</tt> is called whenever a division by zero occurs or when a square root operation is applied to a negative number. <tt>calculate()</tt> applies a binary operator (<b>+</b>, <b>-</b>, <b>�</b>, or <b>�</b>).</p>
 
71
<pre>&nbsp;       double sumInMemory;
 
72
        double sumSoFar;
 
73
        double factorSoFar;
 
74
        QString pendingAdditiveOperator;
 
75
        QString pendingMultiplicativeOperator;
 
76
        bool waitingForOperand;</pre>
 
77
<p>These variables, together with the contents of the calculator display (a <a href="qlineedit.html">QLineEdit</a>), encode the state of the calculator:</p>
 
78
<ul>
 
79
<li><tt>sumInMemory</tt> contains the value stored in the calculator's memory (using <b>MS</b>, <b>M+</b>, or <b>MC</b>).</li>
 
80
<li><tt>sumSoFar</tt> stores the value accumulated so far. When the user clicks <b>=</b>, <tt>sumSoFar</tt> is recomputed and shown on the display. <b>Clear All</b> resets <tt>sumSoFar</tt> to zero.</li>
 
81
<li><tt>factorSoFar</tt> stores a temporary value when doing multiplications and divisions.</li>
 
82
<li><tt>pendingAdditiveOperator</tt> stores the last additive operator clicked by the user.</li>
 
83
<li><tt>pendingMultiplicativeOperator</tt> stores the last multiplicative operator clicked by the user.</li>
 
84
<li><tt>waitingForOperand</tt> is <tt>true</tt> when the calculator is expecting the user to start typing an operand.</li>
 
85
</ul>
 
86
<p>Additive and multiplicative operators are treated differently because they have different precedences. For example, <b>1 + 2 � 3</b> is interpreted as <b>1 + (2 � 3)</b> because <b>�</b> has higher precedence than <b>+</b>.</p>
 
87
<p>The table below shows the evolution of the calculator state as the user enters a mathematical expression.</p>
 
88
<table align="center" cellpadding="2" cellspacing="1" border="0">
 
89
<tr valign="top" bgcolor="#a2c511"><th>User Input</th><th>Display</th><th>Sum so Far</th><th>Add. Op.</th><th>Factor so Far</th><th>Mult. Op.</th><th>Waiting for Operand?</th></tr>
 
90
<tr valign="top" bgcolor="#f0f0f0"><td></td><td>0</td><td>0</td><td></td><td></td><td></td><td><tt>true</tt></td></tr>
 
91
<tr valign="top" bgcolor="#e0e0e0"><td><b>1</b></td><td>1</td><td>0</td><td></td><td></td><td></td><td><tt>false</tt></td></tr>
 
92
<tr valign="top" bgcolor="#f0f0f0"><td><b>1 +</b></td><td>1</td><td>1</td><td><b>+</b></td><td></td><td></td><td><tt>true</tt></td></tr>
 
93
<tr valign="top" bgcolor="#e0e0e0"><td><b>1 + 2</b></td><td>2</td><td>1</td><td><b>+</b></td><td></td><td></td><td><tt>false</tt></td></tr>
 
94
<tr valign="top" bgcolor="#f0f0f0"><td><b>1 + 2 �</b></td><td>2</td><td>1</td><td><b>+</b></td><td>2</td><td><b>�</b></td><td><tt>true</tt></td></tr>
 
95
<tr valign="top" bgcolor="#e0e0e0"><td><b>1 + 2 � 3</b></td><td>3</td><td>1</td><td><b>+</b></td><td>2</td><td><b>�</b></td><td><tt>false</tt></td></tr>
 
96
<tr valign="top" bgcolor="#f0f0f0"><td><b>1 + 2 � 3 -</b></td><td>1.66667</td><td>1.66667</td><td><b>-</b></td><td></td><td></td><td><tt>true</tt></td></tr>
 
97
<tr valign="top" bgcolor="#e0e0e0"><td><b>1 + 2 � 3 - 4</b></td><td>4</td><td>1.66667</td><td><b>-</b></td><td></td><td></td><td><tt>false</tt></td></tr>
 
98
<tr valign="top" bgcolor="#f0f0f0"><td><b>1 + 2 � 3 - 4 =</b></td><td>-2.33333</td><td>0</td><td></td><td></td><td></td><td><tt>true</tt></td></tr>
 
99
</table>
 
100
<p>Unary operators, such as <b>Sqrt</b>, require no special handling; they can be applied immediately since the operand is already known when the operator button is clicked.</p>
 
101
<pre>&nbsp;       QLineEdit *display;
 
102
 
 
103
        enum { NumDigitButtons = 10 };
 
104
        Button *digitButtons[NumDigitButtons];
 
105
 
 
106
        Button *pointButton;
 
107
        Button *changeSignButton;
 
108
        Button *backspaceButton;
 
109
        Button *clearButton;
 
110
        Button *clearAllButton;
 
111
        Button *clearMemoryButton;
 
112
        Button *readMemoryButton;
 
113
        Button *setMemoryButton;
 
114
        Button *addToMemoryButton;
 
115
 
 
116
        Button *divisionButton;
 
117
        Button *timesButton;
 
118
        Button *minusButton;
 
119
        Button *plusButton;
 
120
        Button *squareRootButton;
 
121
        Button *powerButton;
 
122
        Button *reciprocalButton;
 
123
        Button *equalButton;
 
124
    };</pre>
 
125
<p>Finally, we declare the variables associated with the child widgets.</p>
 
126
<a name="calculator-class-implementation"></a>
 
127
<h2>Calculator Class Implementation</h2>
 
128
<pre>&nbsp;   Calculator::Calculator(QWidget *parent)
 
129
        : QDialog(parent)
 
130
    {
 
131
        sumInMemory = 0.0;
 
132
        sumSoFar = 0.0;
 
133
        factorSoFar = 0.0;
 
134
        waitingForOperand = true;</pre>
 
135
<p>In the constructor, we initialize the calculator's state. The <tt>pendingAdditiveOperator</tt> and <tt>pendingMultiplicativeOperator</tt> variables don't need to be initialized explicitly, because the <a href="qstring.html">QString</a> constructor initializes them to empty strings.</p>
 
136
<pre>&nbsp;       display = new QLineEdit(&quot;0&quot;);
 
137
        display-&gt;setReadOnly(true);
 
138
        display-&gt;setAlignment(Qt::AlignRight);
 
139
        display-&gt;setMaxLength(15);
 
140
        display-&gt;installEventFilter(this);
 
141
 
 
142
        QFont font = display-&gt;font();
 
143
        font.setPointSize(font.pointSize() + 8);
 
144
        display-&gt;setFont(font);</pre>
 
145
<p>We create the <a href="qlineedit.html">QLineEdit</a> representing the calculator's display and set up some of its properties. In particular, we set it to be read-only. The <a href="qobject.html#installEventFilter">QObject::installEventFilter</a>() call installs the <tt>Calculator</tt> object (<tt>this</tt>) as an event filter for <tt>display</tt>; we will come back to it later.</p>
 
146
<p>We also enlarge <tt>display</tt>'s font by 8 points.</p>
 
147
<pre>&nbsp;       QColor digitColor(150, 205, 205);
 
148
        QColor backspaceColor(225, 185, 135);
 
149
        QColor memoryColor(100, 155, 155);
 
150
        QColor operatorColor(155, 175, 195);
 
151
 
 
152
        for (int i = 0; i &lt; NumDigitButtons; ++i) {
 
153
            digitButtons[i] = createButton(QString::number(i), digitColor,
 
154
                                           SLOT(digitClicked()));
 
155
        }
 
156
 
 
157
        pointButton = createButton(tr(&quot;.&quot;), digitColor, SLOT(pointClicked()));
 
158
        changeSignButton = createButton(tr(&quot;�&quot;), digitColor, SLOT(changeSignClicked()));
 
159
 
 
160
        backspaceButton = createButton(tr(&quot;Backspace&quot;), backspaceColor,
 
161
                                       SLOT(backspaceClicked()));
 
162
        clearButton = createButton(tr(&quot;Clear&quot;), backspaceColor, SLOT(clear()));
 
163
        clearAllButton = createButton(tr(&quot;Clear All&quot;), backspaceColor.light(120),
 
164
                                      SLOT(clearAll()));
 
165
 
 
166
        clearMemoryButton = createButton(tr(&quot;MC&quot;), memoryColor,
 
167
                                         SLOT(clearMemory()));
 
168
        readMemoryButton = createButton(tr(&quot;MR&quot;), memoryColor, SLOT(readMemory()));
 
169
        setMemoryButton = createButton(tr(&quot;MS&quot;), memoryColor, SLOT(setMemory()));
 
170
        addToMemoryButton = createButton(tr(&quot;M+&quot;), memoryColor,
 
171
                                         SLOT(addToMemory()));
 
172
 
 
173
        divisionButton = createButton(tr(&quot;�&quot;), operatorColor,
 
174
                                      SLOT(multiplicativeOperatorClicked()));
 
175
        timesButton = createButton(tr(&quot;�&quot;), operatorColor,
 
176
                                   SLOT(multiplicativeOperatorClicked()));
 
177
        minusButton = createButton(tr(&quot;-&quot;), operatorColor,
 
178
                                   SLOT(additiveOperatorClicked()));
 
179
        plusButton = createButton(tr(&quot;+&quot;), operatorColor,
 
180
                                  SLOT(additiveOperatorClicked()));
 
181
 
 
182
        squareRootButton = createButton(tr(&quot;Sqrt&quot;), operatorColor,
 
183
                                        SLOT(unaryOperatorClicked()));
 
184
        powerButton = createButton(tr(&quot;x�&quot;), operatorColor,
 
185
                                   SLOT(unaryOperatorClicked()));
 
186
        reciprocalButton = createButton(tr(&quot;1/x&quot;), operatorColor,
 
187
                                        SLOT(unaryOperatorClicked()));
 
188
        equalButton = createButton(tr(&quot;=&quot;), operatorColor.light(120),
 
189
                                   SLOT(equalClicked()));</pre>
 
190
<p>We define four colors by specifying the red, green, and blue components on a scale from 0 to 255. Then, for each button, we call the private <tt>createButton()</tt> function with the proper text label, the associated color, and a slot to connect to the button.</p>
 
191
<p>To make the <b>Clear All</b> and <b>=</b> buttons stand out, we call <a href="qcolor.html#light">QColor::light</a>() with a factor of 120%, making these buttons 20% brighter than their neighbors.</p>
 
192
<pre>&nbsp;       QGridLayout *mainLayout = new QGridLayout;
 
193
        mainLayout-&gt;setSizeConstraint(QLayout::SetFixedSize);
 
194
 
 
195
        mainLayout-&gt;addWidget(display, 0, 0, 1, 6);
 
196
        mainLayout-&gt;addWidget(backspaceButton, 1, 0, 1, 2);
 
197
        mainLayout-&gt;addWidget(clearButton, 1, 2, 1, 2);
 
198
        mainLayout-&gt;addWidget(clearAllButton, 1, 4, 1, 2);
 
199
 
 
200
        mainLayout-&gt;addWidget(clearMemoryButton, 2, 0);
 
201
        mainLayout-&gt;addWidget(readMemoryButton, 3, 0);
 
202
        mainLayout-&gt;addWidget(setMemoryButton, 4, 0);
 
203
        mainLayout-&gt;addWidget(addToMemoryButton, 5, 0);
 
204
 
 
205
        for (int i = 1; i &lt; NumDigitButtons; ++i) {
 
206
            int row = ((9 - i) / 3) + 2;
 
207
            int column = ((i - 1) % 3) + 1;
 
208
            mainLayout-&gt;addWidget(digitButtons[i], row, column);
 
209
        }
 
210
 
 
211
        mainLayout-&gt;addWidget(digitButtons[0], 5, 1);
 
212
        mainLayout-&gt;addWidget(pointButton, 5, 2);
 
213
        mainLayout-&gt;addWidget(changeSignButton, 5, 3);
 
214
 
 
215
        mainLayout-&gt;addWidget(divisionButton, 2, 4);
 
216
        mainLayout-&gt;addWidget(timesButton, 3, 4);
 
217
        mainLayout-&gt;addWidget(minusButton, 4, 4);
 
218
        mainLayout-&gt;addWidget(plusButton, 5, 4);
 
219
 
 
220
        mainLayout-&gt;addWidget(squareRootButton, 2, 5);
 
221
        mainLayout-&gt;addWidget(powerButton, 3, 5);
 
222
        mainLayout-&gt;addWidget(reciprocalButton, 4, 5);
 
223
        mainLayout-&gt;addWidget(equalButton, 5, 5);
 
224
        setLayout(mainLayout);
 
225
 
 
226
        setWindowTitle(tr(&quot;Calculator&quot;));
 
227
    }</pre>
 
228
<p>The layout is handled by a single <a href="qgridlayout.html">QGridLayout</a>. The <a href="qlayout.html#sizeConstraint-prop">QLayout::setSizeConstraint</a>() call ensures that the <tt>Calculator</tt> widget is always shown as its optimal size (its <a href="qwidget.html#sizeHint-prop">size hint</a>), preventing the user from resizing the calculator. The size hint is determined by the size and <a href="qwidget.html#sizePolicy-prop">size policy</a> of the child widgets.</p>
 
229
<p>Most child widgets occupy only one cell in the grid layout. For these, we only need to pass a row and a column to <a href="qgridlayout.html#addWidget">QGridLayout::addWidget</a>(). The <tt>display</tt>, <tt>backspaceButton</tt>, <tt>clearButton</tt>, and <tt>clearAllButton</tt> widgets occupy more than one column; for these we must also pass a row span and a column span.</p>
 
230
<pre>&nbsp;   void Calculator::digitClicked()
 
231
    {
 
232
        Button *clickedButton = qobject_cast&lt;Button *&gt;(sender());
 
233
        int digitValue = clickedButton-&gt;text().toInt();
 
234
        if (display-&gt;text() == &quot;0&quot; &amp;&amp; digitValue == 0.0)
 
235
            return;
 
236
 
 
237
        if (waitingForOperand) {
 
238
            display-&gt;clear();
 
239
            waitingForOperand = false;
 
240
        }
 
241
        display-&gt;setText(display-&gt;text() + QString::number(digitValue));
 
242
    }</pre>
 
243
<p>Pressing one of the calculator's digit buttons will emit the button's <a href="qabstractbutton.html#clicked">clicked()</a> signal, which will trigger the <tt>digitClicked()</tt> slot.</p>
 
244
<p>First, we find out which button sent the signal using <a href="qobject.html#sender">QObject::sender</a>(). This function returns the sender as a <a href="qobject.html">QObject</a> pointer. Since we know that the sender is a <tt>Button</tt> object, we can safely cast the <a href="qobject.html">QObject</a>. We could have used a C-style cast or a C++ <tt>static_cast&lt;&gt;()</tt>, but as a defensive programming technique we use a <a href="qobject.html#qobject_cast">qobject_cast</a>(). The advantage is that if the object has the wrong type, a null pointer is returned. Crashes due to null pointers are much easier to diagnose than crashes due to unsafe casts. Once we have the button, we extract the operator using <a href="qabstractbutton.html#text-prop">QToolButton::text</a>().</p>
 
245
<p>The slot needs to consider two situations in particular. If <tt>display</tt> contains &quot;0&quot; and the user clicks the <b>0</b> button, it would be silly to show &quot;00&quot;. And if the calculator is in a state where it is waiting for a new operand, the new digit is the first digit of that new operand; in that case, any result of a previous calculation must be cleared first.</p>
 
246
<p>At the end, we append the new digit to the value in the display.</p>
 
247
<pre>&nbsp;   void Calculator::unaryOperatorClicked()
 
248
    {
 
249
        Button *clickedButton = qobject_cast&lt;Button *&gt;(sender());
 
250
        QString clickedOperator = clickedButton-&gt;text();
 
251
        double operand = display-&gt;text().toDouble();
 
252
        double result;
 
253
 
 
254
        if (clickedOperator == tr(&quot;Sqrt&quot;)) {
 
255
            if (operand &lt; 0.0) {
 
256
                abortOperation();
 
257
                return;
 
258
            }
 
259
            result = sqrt(operand);
 
260
        } else if (clickedOperator == tr(&quot;x�&quot;)) {
 
261
            result = pow(operand, 2.0);
 
262
        } else if (clickedOperator == tr(&quot;1/x&quot;)) {
 
263
            if (operand == 0.0) {
 
264
                abortOperation();
 
265
                return;
 
266
            }
 
267
            result = 1.0 / operand;
 
268
        }
 
269
        display-&gt;setText(QString::number(result));
 
270
        waitingForOperand = true;
 
271
    }</pre>
 
272
<p>The <tt>unaryOperatorClicked()</tt> slot is called whenever one of the unary operator buttons is clicked. Again a pointer to the clicked button is retrieved using <a href="qobject.html#sender">QObject::sender</a>(). The operator is extracted from the button's text and stored in <tt>clickedOperator</tt>. The operand is obtained from <tt>display</tt>.</p>
 
273
<p>Then we perform the operation. If <b>Sqrt</b> is applied to a negative number or <b>1/x</b> to zero, we call <tt>abortOperation()</tt>. If everything goes well, we display the result of the operation in the line edit and we set <tt>waitingForOperand</tt> to <tt>true</tt>. This ensures that if the user types a new digit, the digit will be considered as a new operand, instead of being appended to the current value.</p>
 
274
<pre>&nbsp;   void Calculator::additiveOperatorClicked()
 
275
    {
 
276
        Button *clickedButton = qobject_cast&lt;Button *&gt;(sender());
 
277
        QString clickedOperator = clickedButton-&gt;text();
 
278
        double operand = display-&gt;text().toDouble();</pre>
 
279
<p>The <tt>additiveOperatorClicked()</tt> slot is called when the user clicks the <b>+</b> or <b>-</b> button.</p>
 
280
<p>Before we can actually do something about the clicked operator, we must handle any pending operations. We start with the multiplicative operators, since these have higher precedence than additive operators:</p>
 
281
<pre>&nbsp;       if (!pendingMultiplicativeOperator.isEmpty()) {
 
282
            if (!calculate(operand, pendingMultiplicativeOperator)) {
 
283
                abortOperation();
 
284
                return;
 
285
            }
 
286
            display-&gt;setText(QString::number(factorSoFar));
 
287
            operand = factorSoFar;
 
288
            factorSoFar = 0.0;
 
289
            pendingMultiplicativeOperator.clear();
 
290
        }</pre>
 
291
<p>If <b>�</b> or <b>�</b> has been clicked earlier, without clicking <b>=</b> afterward, the current value in the display is the right operand of the <b>�</b> or <b>�</b> operator and we can finally perform the operation and update the display.</p>
 
292
<pre>&nbsp;       if (!pendingAdditiveOperator.isEmpty()) {
 
293
            if (!calculate(operand, pendingAdditiveOperator)) {
 
294
                abortOperation();
 
295
                return;
 
296
            }
 
297
            display-&gt;setText(QString::number(sumSoFar));
 
298
        } else {
 
299
            sumSoFar = operand;
 
300
        }</pre>
 
301
<p>If <b>+</b> or <b>-</b> has been clicked earlier, <tt>sumSoFar</tt> is the left operand and the current value in the display is the right operand of the operator. If there is no pending additive operator, <tt>sumSoFar</tt> is simply set to be the text in the display.</p>
 
302
<pre>&nbsp;       pendingAdditiveOperator = clickedOperator;
 
303
        waitingForOperand = true;
 
304
    }</pre>
 
305
<p>Finally, we can take care of the operator that was just clicked. Since we don't have the right-hand operand yet, we store the clicked operator in the <tt>pendingAdditiveOperator</tt> variable. We will apply the operation later, when we have a right operand, with <tt>sumSoFar</tt> as the left operand.</p>
 
306
<pre>&nbsp;   void Calculator::multiplicativeOperatorClicked()
 
307
    {
 
308
        Button *clickedButton = qobject_cast&lt;Button *&gt;(sender());
 
309
        QString clickedOperator = clickedButton-&gt;text();
 
310
        double operand = display-&gt;text().toDouble();
 
311
 
 
312
        if (!pendingMultiplicativeOperator.isEmpty()) {
 
313
            if (!calculate(operand, pendingMultiplicativeOperator)) {
 
314
                abortOperation();
 
315
                return;
 
316
            }
 
317
            display-&gt;setText(QString::number(factorSoFar));
 
318
        } else {
 
319
            factorSoFar = operand;
 
320
        }
 
321
 
 
322
        pendingMultiplicativeOperator = clickedOperator;
 
323
        waitingForOperand = true;
 
324
    }</pre>
 
325
<p>The <tt>multiplicativeOperatorClicked()</tt> slot is similar to <tt>additiveOperatorClicked()</tt>. We don't need to worry about pending additive operators here, because multiplicative operators have precedence over additive operators.</p>
 
326
<pre>&nbsp;   void Calculator::equalClicked()
 
327
    {
 
328
        double operand = display-&gt;text().toDouble();
 
329
 
 
330
        if (!pendingMultiplicativeOperator.isEmpty()) {
 
331
            if (!calculate(operand, pendingMultiplicativeOperator)) {
 
332
                abortOperation();
 
333
                return;
 
334
            }
 
335
            operand = factorSoFar;
 
336
            factorSoFar = 0.0;
 
337
            pendingMultiplicativeOperator.clear();
 
338
        }
 
339
        if (!pendingAdditiveOperator.isEmpty()) {
 
340
            if (!calculate(operand, pendingAdditiveOperator)) {
 
341
                abortOperation();
 
342
                return;
 
343
            }
 
344
            pendingAdditiveOperator.clear();
 
345
        } else {
 
346
            sumSoFar = operand;
 
347
        }
 
348
 
 
349
        display-&gt;setText(QString::number(sumSoFar));
 
350
        sumSoFar = 0.0;
 
351
        waitingForOperand = true;
 
352
    }</pre>
 
353
<p>Like in <tt>additiveOperatorClicked()</tt>, we start by handing any pending multiplicative and additive operators. Then we display <tt>sumSoFar</tt> and reset the variable to zero. Resetting the variable to zero is necessary to avoid counting the value twice.</p>
 
354
<pre>&nbsp;   void Calculator::pointClicked()
 
355
    {
 
356
        if (waitingForOperand)
 
357
            display-&gt;setText(&quot;0&quot;);
 
358
        if (!display-&gt;text().contains(&quot;.&quot;))
 
359
            display-&gt;setText(display-&gt;text() + tr(&quot;.&quot;));
 
360
        waitingForOperand = false;
 
361
    }</pre>
 
362
<p>The <tt>pointClicked()</tt> slot adds a decimal point to the content in <tt>display</tt>.</p>
 
363
<pre>&nbsp;   void Calculator::changeSignClicked()
 
364
    {
 
365
        QString text = display-&gt;text();
 
366
        double value = text.toDouble();
 
367
 
 
368
        if (value &gt; 0.0) {
 
369
            text.prepend(tr(&quot;-&quot;));
 
370
        } else if (value &lt; 0.0) {
 
371
            text.remove(0, 1);
 
372
        }
 
373
        display-&gt;setText(text);
 
374
    }</pre>
 
375
<p>The <tt>changeSignClicked()</tt> slot changes the sign of the value in <tt>display</tt>. If the current value is positive, we prepend a minus sign; if the current value is negative, we remove the first character from the value (the minus sign).</p>
 
376
<pre>&nbsp;   void Calculator::backspaceClicked()
 
377
    {
 
378
        if (waitingForOperand)
 
379
            return;
 
380
 
 
381
        QString text = display-&gt;text();
 
382
        text.chop(1);
 
383
        if (text.isEmpty()) {
 
384
            text = &quot;0&quot;;
 
385
            waitingForOperand = true;
 
386
        }
 
387
        display-&gt;setText(text);
 
388
    }</pre>
 
389
<p>The <tt>backspaceClicked()</tt> removes the rightmost character in the display. If we get an empty string, we show &quot;0&quot; and set <tt>waitingForOperand</tt> to <tt>true</tt>.</p>
 
390
<pre>&nbsp;   void Calculator::clear()
 
391
    {
 
392
        if (waitingForOperand)
 
393
            return;
 
394
 
 
395
        display-&gt;setText(&quot;0&quot;);
 
396
        waitingForOperand = true;
 
397
    }</pre>
 
398
<p>The <tt>clear()</tt> slot resets the current operand to zero. It is equivalent to clicking <b>Backspace</b> enough times to erase the entire operand.</p>
 
399
<pre>&nbsp;   void Calculator::clearAll()
 
400
    {
 
401
        sumSoFar = 0.0;
 
402
        factorSoFar = 0.0;
 
403
        pendingAdditiveOperator.clear();
 
404
        pendingMultiplicativeOperator.clear();
 
405
        display-&gt;setText(&quot;0&quot;);
 
406
        waitingForOperand = true;
 
407
    }</pre>
 
408
<p>The <tt>clearAll()</tt> slot resets the calculator to its initial state.</p>
 
409
<pre>&nbsp;   void Calculator::clearMemory()
 
410
    {
 
411
        sumInMemory = 0.0;
 
412
    }
 
413
 
 
414
    void Calculator::readMemory()
 
415
    {
 
416
        display-&gt;setText(QString::number(sumInMemory));
 
417
        waitingForOperand = true;
 
418
    }
 
419
 
 
420
    void Calculator::setMemory()
 
421
    {
 
422
        equalClicked();
 
423
        sumInMemory = display-&gt;text().toDouble();
 
424
    }
 
425
 
 
426
    void Calculator::addToMemory()
 
427
    {
 
428
        equalClicked();
 
429
        sumInMemory += display-&gt;text().toDouble();
 
430
    }</pre>
 
431
<p>The <tt>clearMemory()</tt> slot erases the sum kept in memory, <tt>readMemory()</tt> displays the sum as an operand, <tt>setMemory()</tt> replace the sum in memory with the current sum, and <tt>addToMemory()</tt> adds the current value to the value in memory. For <tt>setMemory()</tt> and <tt>addToMemory()</tt>, we start by calling <tt>equalClicked()</tt> to update <tt>sumSoFar</tt> and the value in the display.</p>
 
432
<pre>&nbsp;   Button *Calculator::createButton(const QString &amp;text, const QColor &amp;color,
 
433
                                     const char *member)
 
434
    {
 
435
        Button *button = new Button(text, color);
 
436
        connect(button, SIGNAL(clicked()), this, member);
 
437
        return button;
 
438
    }</pre>
 
439
<p>The private <tt>createButton()</tt> function is called from the constructor to create calculator buttons.</p>
 
440
<pre>&nbsp;   void Calculator::abortOperation()
 
441
    {
 
442
        clearAll();
 
443
        display-&gt;setText(tr(&quot;####&quot;));
 
444
    }</pre>
 
445
<p>The private <tt>abortOperation()</tt> function is called whenever a calculation fails. It resets the calculator state and displays &quot;####&quot;.</p>
 
446
<pre>&nbsp;   bool Calculator::calculate(double rightOperand, const QString &amp;pendingOperator)
 
447
    {
 
448
        if (pendingOperator == tr(&quot;+&quot;)) {
 
449
            sumSoFar += rightOperand;
 
450
        } else if (pendingOperator == tr(&quot;-&quot;)) {
 
451
            sumSoFar -= rightOperand;
 
452
        } else if (pendingOperator == tr(&quot;�&quot;)) {
 
453
            factorSoFar *= rightOperand;
 
454
        } else if (pendingOperator == tr(&quot;�&quot;)) {
 
455
            if (rightOperand == 0.0)
 
456
                return false;
 
457
            factorSoFar /= rightOperand;
 
458
        }
 
459
        return true;
 
460
    }</pre>
 
461
<p>The private <tt>calculate()</tt> function performs a binary operation. The right operand is given by <tt>rightOperand</tt>. For additive operators, the left operand is <tt>sumSoFar</tt>; for multiplicative operators, the left operand is <tt>factorSoFar</tt>. The function return <tt>false</tt> if a division by zero occurs.</p>
 
462
<pre>&nbsp;   bool Calculator::eventFilter(QObject *target, QEvent *event)
 
463
    {
 
464
        if (target == display) {
 
465
            if (event-&gt;type() == QEvent::MouseButtonPress
 
466
                    || event-&gt;type() == QEvent::MouseButtonDblClick
 
467
                    || event-&gt;type() == QEvent::MouseButtonRelease
 
468
                    || event-&gt;type() == QEvent::ContextMenu) {
 
469
                QMouseEvent *mouseEvent = static_cast&lt;QMouseEvent *&gt;(event);
 
470
                if (mouseEvent-&gt;buttons() &amp; Qt::LeftButton) {
 
471
                    QPalette newPalette = palette();
 
472
                    newPalette.setColor(QPalette::Base,
 
473
                                        display-&gt;palette().color(QPalette::Text));
 
474
                    newPalette.setColor(QPalette::Text,
 
475
                                        display-&gt;palette().color(QPalette::Base));
 
476
                    display-&gt;setPalette(newPalette);
 
477
                } else {
 
478
                    display-&gt;setPalette(palette());
 
479
                }
 
480
                return true;
 
481
            }
 
482
        }
 
483
        return QDialog::eventFilter(target, event);
 
484
    }</pre>
 
485
<p>The <tt>eventFilter()</tt> function is inherited from <a href="qobject.html">QObject</a>. In the constructor, we installed it on <tt>display</tt>. While the user holds the left mouse button pressed, we use an inverted color scheme. Any other events are ignored and simply passed on to the base class's <a href="qobject.html#eventFilter">eventFilter()</a> implementation.</p>
 
486
<a name="button-class-definition"></a>
 
487
<h2>Button Class Definition</h2>
 
488
<p>Let's now take a look at the <tt>Button</tt> class:</p>
 
489
<pre>&nbsp;   class Button : public QToolButton
 
490
    {
 
491
        Q_OBJECT
 
492
 
 
493
    public:
 
494
        Button(const QString &amp;text, const QColor &amp;color, QWidget *parent = 0);
 
495
 
 
496
        QSize sizeHint() const;
 
497
    };</pre>
 
498
<p>The <tt>Button</tt> class has a convenience constructor that takes a text label and a color, and it reimplements <a href="qwidget.html#sizeHint-prop">QWidget::sizeHint</a>() to provide more space around the text than what <a href="qtoolbutton.html">QToolButton</a> normally provides.</p>
 
499
<a name="button-class-implementation"></a>
 
500
<h2>Button Class Implementation</h2>
 
501
<pre>&nbsp;   Button::Button(const QString &amp;text, const QColor &amp;color, QWidget *parent)
 
502
        : QToolButton(parent)
 
503
    {
 
504
        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
 
505
        setText(text);
 
506
 
 
507
        QPalette newPalette = palette();
 
508
        newPalette.setColor(QPalette::Button, color);
 
509
        setPalette(newPalette);
 
510
    }</pre>
 
511
<p>The buttons' appearance is determined by the layout of the calculator widget through the size and <a href="qwidget.html#sizePolicy-prop">size policy</a> of the layout's child widgets. The call to the <a href="qwidget.html#sizePolicy-prop">setSizePolicy()</a> function in the constructor ensures that the button will expand horizontally to fill all the available space; by default, <a href="qtoolbutton.html">QToolButton</a>s don't expand to fill available space. Without this call, the different buttons in a same column would have different widths.</p>
 
512
<pre>&nbsp;   QSize Button::sizeHint() const
 
513
    {
 
514
        QSize size = QToolButton::sizeHint();
 
515
        size.rheight() += 20;
 
516
        size.rwidth() = qMax(size.width(), size.height());
 
517
        return size;
 
518
    }</pre>
 
519
<p>In <a href="qwidget.html#sizeHint-prop">sizeHint()</a>, we try to return a size that looks good for most buttons. We reuse the size hint of the base class (<a href="qtoolbutton.html">QToolButton</a>) but modify it in the following ways:</p>
 
520
<ul>
 
521
<li>We add 20 to the <a href="qsize.html#height">height</a> component of the size hint.</li>
 
522
<li>We make the <a href="qsize.html#width">width</a> component of the size hint at least as much as the <a href="qsize.html#width">height</a>.</li>
 
523
</ul>
 
524
<p>This ensures that with most fonts, the digit and operator buttons will be square, without truncating the text on the <b>Backspace</b>, <b>Clear</b>, and <b>Clear All</b> buttons.</p>
 
525
<p>The screenshot below shows how the <tt>Calculator</tt> widget would would like if we <i>didn't</i> set the horizontal size policy to <a href="qsizepolicy.html#Policy-enum">QSizePolicy::Expanding</a> in the constructor and if we didn't reimplement <a href="qwidget.html#sizeHint-prop">QWidget::sizeHint</a>().</p>
 
526
<center><img src="images/calculator-ugly.png" alt="The Calculator example with default size policies and size hints" /></center><p>This completes the Calculator example. Trolltech will send a T-shirt to the first finder of a bug in the example.</p>
 
527
<p /><address><hr /><div align="center">
 
528
<table width="100%" cellspacing="0" border="0"><tr class="address">
 
529
<td width="30%">Copyright &copy; 2005 <a href="trolltech.html">Trolltech</a></td>
 
530
<td width="40%" align="center"><a href="trademarks.html">Trademarks</a></td>
 
531
<td width="30%" align="right"><div align="right">Qt 4.0.0</div></td>
 
532
</tr></table></div></address></body>
 
533
</html>