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. It is also possible to initialize those variable directly in the header. This is called member-initializaton and avoids a long initialization list.
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
, 4
);
mainLayout