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 &text, const char *member);
void abortOperation();
bool calculate(double rightOperand, const QString &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->setReadOnly(true);
display->setAlignment(Qt::AlignRight);
display->setMaxLength(15);
QFont font = display->font();
font.setPointSize(font.pointSize() + 8);
display->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 < 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->setSizeConstraint(QLayout::SetFixedSize);
mainLayout->addWidget(display, 0, 0, 1, 6);
mainLayout->addWidget(backspaceButton, 1, 0, 1, 2);
mainLayout->addWidget(clearButton, 1, 2, 1, 2);
mainLayout->addWidget(clearAllButton, 1, 4, 1, 2);
mainLayout->addWidget(clearMemoryButton, 2, 0);
mainLayout->addWidget(readMemoryButton, 3, 0);
mainLayout->addWidget(setMemoryButton, 4, 0);
mainLayout->addWidget(addToMemoryButton, 5, 0);
for (int i = 1; i < NumDigitButtons; ++i) {
int row = ((9 - i) / 3) + 2;
int column = ((i - 1) % 3) + 1;
mainLayout->addWidget(digitButtons[i], row, column);
}
mainLayout->addWidget(digitButtons[0], 5, 1);
mainLayout->addWidget(pointButton, 5, 2);
mainLayout->addWidget(changeSignButton, 5, 3);
mainLayout->addWidget(divisionButton, 2, 4);
mainLayout->addWidget(timesButton, 3, 4);
mainLayout->addWidget(minusButton, 4, 

