Calculator Example▲

The example consists of two classes:
-
Calculator is the calculator widget, with all the calculator functionality.
-
Button is the widget used for each of the calculator button. It derives from QToolButton.
We will start by reviewing Calculator, then we will take a look at Button.
Calculator Class Definition▲
class
Calculator : public
QWidget
{
Q_OBJECT
public
:
Calculator(QWidget *
parent =
nullptr
);
private
slots:
void
digitClicked();
void
unaryOperatorClicked();
void
additiveOperatorClicked();
void
multiplicativeOperatorClicked();
void
equalClicked();
void
pointClicked();
void
changeSignClicked();
void
backspaceClicked();
void
clear();
void
clearAll();
void
clearMemory();
void
readMemory();
void
setMemory();
void
addToMemory();
The Calculator class provides a simple calculator widget. It inherits from QDialog and has several private slots associated with the calculator's buttons. QObject::eventFilter() is reimplemented to handle mouse events on the calculator's display.
Buttons are grouped in categories according to their behavior. For example, all the digit buttons (labeled 0 to 9) append a digit to the current operand. For these, we connect multiple buttons to the same slot (e.g., digitClicked()). The categories are digits, unary operators (Sqrt, x², 1/x), additive operators (+, -), and multiplicative operators (×, ÷). The other buttons have their own slots.
private
:
Button *
createButton(const
QString &
amp;text, const
char
*
member);
void
abortOperation();
bool
calculate(double
rightOperand, const
QString &
amp;pendingOperator);
The private createButton() function is used as part of the widget construction. abortOperation() is called whenever a division by zero occurs or when a square root operation is applied to a negative number. calculate() applies a binary operator (+, -, ×, or ÷).
double
sumInMemory;
double
sumSoFar;
double
factorSoFar;
QString pendingAdditiveOperator;
QString pendingMultiplicativeOperator;
bool
waitingForOperand;
These variables, together with the contents of the calculator display (a QLineEdit), encode the state of the calculator:
-
sumInMemory contains the value stored in the calculator's memory (using MS, M+, or MC).
-
sumSoFar stores the value accumulated so far. When the user clicks =, sumSoFar is recomputed and shown on the display. Clear All resets sumSoFar to zero.
-
factorSoFar stores a temporary value when doing multiplications and divisions.
-
pendingAdditiveOperator stores the last additive operator clicked by the user.
-
pendingMultiplicativeOperator stores the last multiplicative operator clicked by the user.
-
waitingForOperand is true when the calculator is expecting the user to start typing an operand.
Additive and multiplicative operators are treated differently because they have different precedences. For example, 1 + 2 ÷ 3 is interpreted as 1 + (2 ÷ 3) because ÷ has higher precedence than +.
The table below shows the evolution of the calculator state as the user enters a mathematical expression.
User Input |
Display |
Sum so Far |
Add. Op. |
Factor so Far |
Mult. Op. |
Waiting for Operand? |
---|---|---|---|---|---|---|
0 |
0 |
true |
||||
1 |
1 |
0 |
false |
|||
1 + |
1 |
1 |
+ |
true |
||
1 + 2 |
2 |
1 |
+ |
false |
||
1 + 2 ÷ |
2 |
1 |
+ |
2 |
÷ |
true |
1 + 2 ÷ 3 |
3 |
1 |
+ |
2 |
÷ |
false |
1 + 2 ÷ 3 - |
1.66667 |
1.66667 |
- |
true |
||
1 + 2 ÷ 3 - 4 |
4 |
1.66667 |
- |
false |
||
1 + 2 ÷ 3 - 4 = |
-2.33333 |
0 |
true |
Unary operators, such as Sqrt, require no special handling; they can be applied immediately since the operand is already known when the operator button is clicked.
QLineEdit *
display;
enum
{
NumDigitButtons =
10
}
;
Button *
digitButtons[NumDigitButtons];
}
;
Finally, we declare the variables associated with the display and the buttons used to display numerals.
Calculator Class Implementation▲
Calculator::
Calculator(QWidget *
parent)
:
QWidget(parent)
{
sumInMemory =
0.0
;
sumSoFar =
0.0
;
factorSoFar =
0.0
;
waitingForOperand =
true
;
In the constructor, we initialize the calculator's state. The pendingAdditiveOperator and pendingMultiplicativeOperator variables don't need to be initialized explicitly, because the QString constructor initializes them to empty strings.
display =
new
QLineEdit("0"
);
display-&
gt;setReadOnly(true
);
display-&
gt;setAlignment(Qt::
AlignRight);
display-&
gt;setMaxLength(15
);
QFont font =
display-&
gt;font();
font.setPointSize(font.pointSize() +
8
);
display-&
gt;setFont(font);
We create the QLineEdit representing the calculator's display and set up some of its properties. In particular, we set it to be read-only.
We also enlarge display's font by 8 points.
for
(int
i =
0
; i &
lt; NumDigitButtons; ++
i) {
digitButtons[i] =
createButton(QString::
number(i), SLOT(digitClicked()));
}
Button *
pointButton =
createButton(tr("."
), SLOT(pointClicked()));
Button *
changeSignButton =
createButton(tr("
\302\261
"
), SLOT(changeSignClicked()));
Button *
backspaceButton =
createButton(tr("Backspace"
), SLOT(backspaceClicked()));
Button *
clearButton =
createButton(tr("Clear"
), SLOT(clear()));
Button *
clearAllButton =
createButton(tr("Clear All"
), SLOT(clearAll()));
Button *
clearMemoryButton =
createButton(tr("MC"
), SLOT(clearMemory()));
Button *
readMemoryButton =
createButton(tr("MR"
), SLOT(readMemory()));
Button *
setMemoryButton =
createButton(tr("MS"
), SLOT(setMemory()));
Button *
addToMemoryButton =
createButton(tr("M+"
), SLOT(addToMemory()));
Button *
divisionButton =
createButton(tr("
\303\267
"
), SLOT(multiplicativeOperatorClicked()));
Button *
timesButton =
createButton(tr("
\303\227
"
), SLOT(multiplicativeOperatorClicked()));
Button *
minusButton =
createButton(tr("-"
), SLOT(additiveOperatorClicked()));
Button *
plusButton =
createButton(tr("+"
), SLOT(additiveOperatorClicked()));
Button *
squareRootButton =
createButton(tr("Sqrt"
), SLOT(unaryOperatorClicked()));
Button *
powerButton =
createButton(tr("x
\302\262
"
), SLOT(unaryOperatorClicked()));
Button *
reciprocalButton =
createButton(tr("1/x"
), SLOT(unaryOperatorClicked()));
Button *
equalButton =
createButton(tr("="
), SLOT(equalClicked()));
For each button, we call the private createButton() function with the proper text label and a slot to connect to the button.
QGridLayout *
mainLayout =
new
QGridLayout;
mainLayout-&
gt;setSizeConstraint(QLayout::
SetFixedSize);
mainLayout-&
gt;addWidget(display, 0
, 0
, 1
, 6
);
mainLayout-&
gt;addWidget(backspaceButton, 1
, 0
, 1
, 2
);
mainLayout-&
gt;addWidget(clearButton, 1
, 2
, 1
, 2
);
mainLayout-&
gt;addWidget(clearAllButton, 1
, 4
, 1
, 2
);
mainLayout-&
gt;addWidget(clearMemoryButton, 2
, 0
);
mainLayout-&
gt;addWidget(readMemoryButton, 3
, 0
);
mainLayout-&
gt;addWidget(setMemoryButton, 4
, 0
);
mainLayout-&
gt;addWidget(addToMemoryButton, 5
, 0
);
for
(int
i =
1
; i &
lt; NumDigitButtons; ++
i) {
int
row =
((9
-
i) /
3
) +
2
;
int
column =
((i -
1
) %
3
) +
1
;
mainLayout-&
gt;addWidget(digitButtons[i], row, column);
}
mainLayout-&
gt;addWidget(digitButtons[0
], 5
, 1
);
mainLayout-&
gt;addWidget(pointButton, 5
, 2
);
mainLayout-&
gt;addWidget(changeSignButton, 5
, 3
);
mainLayout-&
gt;addWidget(divisionButton, 2
, 4
);
mainLayout-&
gt;addWidget(timesButton, 3
, 4
);
mainLayout-&
gt;addWidget(minusButton, 4
,