根據 http://doc.qt.io/qt-5/qtwidgets-widgets-calculator-example.html 的說明, 利用 Eric6 與 PyQt5 開發簡單的計算器視窗程式.
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.
Finally, we declare the variables associated with the display and the buttons used to display numerals.
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.
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 each button, we call the private createButton() function with the proper text label and a slot to connect to the button.
The layout is handled by a single QGridLayout. The QLayout::setSizeConstraint() call ensures that the Calculator widget is always shown as its optimal size (its size hint), preventing the user from resizing the calculator. The size hint is determined by the size and 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 display, backspaceButton, clearButton, and clearAllButton widgets occupy more than one column; for these we must also pass a row span and a column span.
Pressing one of the calculator's digit buttons will emit the button's clicked() signal, which will trigger the 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 Button object, we can safely cast the QObject. We could have used a C-style cast or a C++ static_cast<>(), but as a defensive programming technique we use a 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 display contains "0" and the user clicks the 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.
The 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 clickedOperator. The operand is obtained from display.
Then we perform the operation. If Sqrt is applied to a negative number or 1/x to zero, we call abortOperation(). If everything goes well, we display the result of the operation in the line edit and we set waitingForOperand to 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.
The additiveOperatorClicked() slot is called when the user clicks the + or - 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:
If × or ÷ has been clicked earlier, without clicking = afterward, the current value in the display is the right operand of the × or ÷ operator and we can finally perform the operation and update the display.
If + or - has been clicked earlier, 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, sumSoFar is simply set to be the text in the display.
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 pendingAdditiveOperator variable. We will apply the operation later, when we have a right operand, with sumSoFar as the left operand.
The multiplicativeOperatorClicked() slot is similar to additiveOperatorClicked(). We don't need to worry about pending additive operators here, because multiplicative operators have precedence over additive operators.
Like in additiveOperatorClicked(), we start by handling any pending multiplicative and additive operators. Then we display sumSoFar and reset the variable to zero. Resetting the variable to zero is necessary to avoid counting the value twice.
The pointClicked() slot adds a decimal point to the content in display.
The changeSignClicked() slot changes the sign of the value in 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).
The backspaceClicked() removes the rightmost character in the display. If we get an empty string, we show "0" and set waitingForOperand to true.
The clear() slot resets the current operand to zero. It is equivalent to clicking Backspace enough times to erase the entire operand.
The clearAll() slot resets the calculator to its initial state.
The clearMemory() slot erases the sum kept in memory, readMemory() displays the sum as an operand, setMemory() replace the sum in memory with the current sum, and addToMemory() adds the current value to the value in memory. For setMemory() and addToMemory(), we start by calling equalClicked() to update sumSoFar and the value in the display.
The private createButton() function is called from the constructor to create calculator buttons.
The private abortOperation() function is called whenever a calculation fails. It resets the calculator state and displays "####".
The private calculate() function performs a binary operation. The right operand is given by rightOperand. For additive operators, the left operand is sumSoFar; for multiplicative operators, the left operand is factorSoFar. The function return false if a division by zero occurs.