qt5base-lts/examples/widgets/doc/src/calculator.qdoc

376 lines
17 KiB
Plaintext
Raw Normal View History

/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: http://www.gnu.org/copyleft/fdl.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example widgets/calculator
\title Calculator Example
The example shows how to use signals and slots to implement the
functionality of a calculator widget, and how to use QGridLayout
to place child widgets in a grid.
\image calculator-example.png Screenshot of the Calculator example
The example consists of two classes:
\list
\li \c Calculator is the calculator widget, with all the
calculator functionality.
\li \c Button is the widget used for each of the calculator
button. It derives from QToolButton.
\endlist
We will start by reviewing \c Calculator, then we will take a
look at \c Button.
\section1 Calculator Class Definition
\snippet widgets/calculator/calculator.h 0
The \c 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 \uicontrol 0 to \uicontrol 9)
append a digit to the current operand. For these, we connect
multiple buttons to the same slot (e.g., \c digitClicked()). The
categories are digits, unary operators (\uicontrol{Sqrt}, \uicontrol{x\unicode{178}},
\uicontrol{1/x}), additive operators (\uicontrol{+}, \uicontrol{-}), and
multiplicative operators (\uicontrol{\unicode{215}}, \uicontrol{\unicode{247}}). The other buttons
have their own slots.
\snippet widgets/calculator/calculator.h 1
\snippet widgets/calculator/calculator.h 2
The private \c createButton() function is used as part of the
widget construction. \c abortOperation() is called whenever a
division by zero occurs or when a square root operation is
applied to a negative number. \c calculate() applies a binary
operator (\uicontrol{+}, \uicontrol{-}, \uicontrol{\unicode{215}}, or \uicontrol{\unicode{247}}).
\snippet widgets/calculator/calculator.h 3
\snippet widgets/calculator/calculator.h 4
\snippet widgets/calculator/calculator.h 5
\snippet widgets/calculator/calculator.h 6
\snippet widgets/calculator/calculator.h 7
\snippet widgets/calculator/calculator.h 8
These variables, together with the contents of the calculator
display (a QLineEdit), encode the state of the calculator:
\list
\li \c sumInMemory contains the value stored in the calculator's memory
(using \uicontrol{MS}, \uicontrol{M+}, or \uicontrol{MC}).
\li \c sumSoFar stores the value accumulated so far. When the user
clicks \uicontrol{=}, \c sumSoFar is recomputed and shown on the
display. \uicontrol{Clear All} resets \c sumSoFar to zero.
\li \c factorSoFar stores a temporary value when doing
multiplications and divisions.
\li \c pendingAdditiveOperator stores the last additive operator
clicked by the user.
\li \c pendingMultiplicativeOperator stores the last multiplicative operator
clicked by the user.
\li \c waitingForOperand is \c true when the calculator is
expecting the user to start typing an operand.
\endlist
Additive and multiplicative operators are treated differently
because they have different precedences. For example, \uicontrol{1 + 2 \unicode{247}
3} is interpreted as \uicontrol{1 + (2 \unicode{247} 3)} because \uicontrol{\unicode{247}} has higher
precedence than \uicontrol{+}.
The table below shows the evolution of the calculator state as
the user enters a mathematical expression.
\table
\header \li User Input \li Display \li Sum so Far \li Add. Op. \li Factor so Far \li Mult. Op. \li Waiting for Operand?
\row \li \li 0 \li 0 \li \li \li \li \c true
\row \li \uicontrol{1} \li 1 \li 0 \li \li \li \li \c false
\row \li \uicontrol{1 +} \li 1 \li 1 \li \uicontrol{+} \li \li \li \c true
\row \li \uicontrol{1 + 2} \li 2 \li 1 \li \uicontrol{+} \li \li \li \c false
\row \li \uicontrol{1 + 2 \unicode{247}} \li 2 \li 1 \li \uicontrol{+} \li 2 \li \uicontrol{\unicode{247}} \li \c true
\row \li \uicontrol{1 + 2 \unicode{247} 3} \li 3 \li 1 \li \uicontrol{+} \li 2 \li \uicontrol{\unicode{247}} \li \c false
\row \li \uicontrol{1 + 2 \unicode{247} 3 -} \li 1.66667 \li 1.66667 \li \uicontrol{-} \li \li \li \c true
\row \li \uicontrol{1 + 2 \unicode{247} 3 - 4} \li 4 \li 1.66667 \li \uicontrol{-} \li \li \li \c false
\row \li \uicontrol{1 + 2 \unicode{247} 3 - 4 =} \li -2.33333 \li 0 \li \li \li \li \c true
\endtable
Unary operators, such as \uicontrol Sqrt, require no special handling;
they can be applied immediately since the operand is already
known when the operator button is clicked.
\snippet widgets/calculator/calculator.h 9
\codeline
\snippet widgets/calculator/calculator.h 10
Finally, we declare the variables associated with the display and the
buttons used to display numerals.
\section1 Calculator Class Implementation
\snippet widgets/calculator/calculator.cpp 0
In the constructor, we initialize the calculator's state. The \c
pendingAdditiveOperator and \c pendingMultiplicativeOperator
variables don't need to be initialized explicitly, because the
QString constructor initializes them to empty strings.
\snippet widgets/calculator/calculator.cpp 1
\snippet widgets/calculator/calculator.cpp 2
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 \c{display}'s font by 8 points.
\snippet widgets/calculator/calculator.cpp 4
For each button, we call the private \c createButton() function with
the proper text label and a slot to connect to the button.
\snippet widgets/calculator/calculator.cpp 5
\snippet widgets/calculator/calculator.cpp 6
The layout is handled by a single QGridLayout. The
QLayout::setSizeConstraint() call ensures that the \c Calculator
widget is always shown as its optimal size (its
\l{QWidget::sizeHint()}{size hint}), preventing the user from
resizing the calculator. The size hint is determined by the size
and \l{QWidget::sizePolicy()}{size policy} of the child widgets.
Most child widgets occupy only one cell in the grid layout. For
these, we only need to pass a row and a column to
QGridLayout::addWidget(). The \c display, \c backspaceButton, \c
clearButton, and \c clearAllButton widgets occupy more than one
column; for these we must also pass a row span and a column
span.
\snippet widgets/calculator/calculator.cpp 7
Pressing one of the calculator's digit buttons will emit the
button's \l{QToolButton::clicked()}{clicked()} signal, which will
trigger the \c digitClicked() slot.
First, we find out which button sent the signal using
QObject::sender(). This function returns the sender as a QObject
pointer. Since we know that the sender is a \c Button object, we
can safely cast the QObject. We could have used a C-style cast or
a C++ \c static_cast<>(), but as a defensive programming
technique we use a \l qobject_cast(). 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 QToolButton::text().
The slot needs to consider two situations in particular. If \c
display contains "0" and the user clicks the \uicontrol{0} button, it
would be silly to show "00". 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.
At the end, we append the new digit to the value in the display.
\snippet widgets/calculator/calculator.cpp 8
\snippet widgets/calculator/calculator.cpp 9
The \c unaryOperatorClicked() slot is called whenever one of the
unary operator buttons is clicked. Again a pointer to the clicked
button is retrieved using QObject::sender(). The operator is
extracted from the button's text and stored in \c
clickedOperator. The operand is obtained from \c display.
Then we perform the operation. If \uicontrol Sqrt is applied to a
negative number or \uicontrol{1/x} to zero, we call \c
abortOperation(). If everything goes well, we display the result
of the operation in the line edit and we set \c waitingForOperand
to \c true. 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.
\snippet widgets/calculator/calculator.cpp 10
\snippet widgets/calculator/calculator.cpp 11
The \c additiveOperatorClicked() slot is called when the user
clicks the \uicontrol{+} or \uicontrol{-} button.
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:
\snippet widgets/calculator/calculator.cpp 12
\snippet widgets/calculator/calculator.cpp 13
If \uicontrol{\unicode{215}} or \uicontrol{\unicode{247}} has been clicked earlier, without clicking
\uicontrol{=} afterward, the current value in the display is the right
operand of the \uicontrol{\unicode{215}} or \uicontrol{\unicode{247}} operator and we can finally
perform the operation and update the display.
\snippet widgets/calculator/calculator.cpp 14
\snippet widgets/calculator/calculator.cpp 15
If \uicontrol{+} or \uicontrol{-} has been clicked earlier, \c sumSoFar 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, \c sumSoFar is simply set to be the text in the
display.
\snippet widgets/calculator/calculator.cpp 16
\snippet widgets/calculator/calculator.cpp 17
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 \c pendingAdditiveOperator variable. We will
apply the operation later, when we have a right operand, with \c
sumSoFar as the left operand.
\snippet widgets/calculator/calculator.cpp 18
The \c multiplicativeOperatorClicked() slot is similar to \c
additiveOperatorClicked(). We don't need to worry about pending
additive operators here, because multiplicative operators have
precedence over additive operators.
\snippet widgets/calculator/calculator.cpp 20
Like in \c additiveOperatorClicked(), we start by handing any
pending multiplicative and additive operators. Then we display \c
sumSoFar and reset the variable to zero. Resetting the variable
to zero is necessary to avoid counting the value twice.
\snippet widgets/calculator/calculator.cpp 22
The \c pointClicked() slot adds a decimal point to the content in
\c display.
\snippet widgets/calculator/calculator.cpp 24
The \c changeSignClicked() slot changes the sign of the value in
\c display. 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).
\snippet widgets/calculator/calculator.cpp 26
The \c backspaceClicked() removes the rightmost character in the
display. If we get an empty string, we show "0" and set \c
waitingForOperand to \c true.
\snippet widgets/calculator/calculator.cpp 28
The \c clear() slot resets the current operand to zero. It is
equivalent to clicking \uicontrol Backspace enough times to erase the
entire operand.
\snippet widgets/calculator/calculator.cpp 30
The \c clearAll() slot resets the calculator to its initial state.
\snippet widgets/calculator/calculator.cpp 32
The \c clearMemory() slot erases the sum kept in memory, \c
readMemory() displays the sum as an operand, \c setMemory()
replace the sum in memory with the current sum, and \c
addToMemory() adds the current value to the value in memory. For
\c setMemory() and \c addToMemory(), we start by calling \c
equalClicked() to update \c sumSoFar and the value in the
display.
\snippet widgets/calculator/calculator.cpp 34
The private \c createButton() function is called from the
constructor to create calculator buttons.
\snippet widgets/calculator/calculator.cpp 36
The private \c abortOperation() function is called whenever a
calculation fails. It resets the calculator state and displays
"####".
\snippet widgets/calculator/calculator.cpp 38
The private \c calculate() function performs a binary operation.
The right operand is given by \c rightOperand. For additive
operators, the left operand is \c sumSoFar; for multiplicative
operators, the left operand is \c factorSoFar. The function
return \c false if a division by zero occurs.
\section1 Button Class Definition
Let's now take a look at the \c Button class:
\snippet widgets/calculator/button.h 0
The \c Button class has a convenience constructor that takes a
text label and a parent widget, and it reimplements QWidget::sizeHint()
to provide more space around the text than the amount QToolButton
normally provides.
\section1 Button Class Implementation
\snippet widgets/calculator/button.cpp 0
The buttons' appearance is determined by the layout of the
calculator widget through the size and
\l{QWidget::sizePolicy}{size policy} of the layout's child
widgets. The call to the
\l{QWidget::setSizePolicy()}{setSizePolicy()} function in the
constructor ensures that the button will expand horizontally to
fill all the available space; by default, \l{QToolButton}s don't
expand to fill available space. Without this call, the different
buttons in a same column would have different widths.
\snippet widgets/calculator/button.cpp 1
\snippet widgets/calculator/button.cpp 2
In \l{QWidget::sizeHint()}{sizeHint()}, we try to return a size
that looks good for most buttons. We reuse the size hint of the
base class (QToolButton) but modify it in the following ways:
\list
\li We add 20 to the \l{QSize::height()}{height} component of the size hint.
\li We make the \l{QSize::width()}{width} component of the size
hint at least as much as the \l{QSize::width()}{height}.
\endlist
This ensures that with most fonts, the digit and operator buttons
will be square, without truncating the text on the
\uicontrol{Backspace}, \uicontrol{Clear}, and \uicontrol{Clear All} buttons.
The screenshot below shows how the \c Calculator widget would
look like if we \e didn't set the horizontal size policy to
QSizePolicy::Expanding in the constructor and if we didn't
reimplement QWidget::sizeHint().
\image calculator-ugly.png The Calculator example with default size policies and size hints
*/