calculator.h
#ifndef CALCULATOR_H
#define CALCULATOR_H
#include <QWidget>
QT_BEGIN_NAMESPACE
class QLineEdit;
QT_END_NAMESPACE
class Button;
//! [0]
class Calculator : public QWidget
{
Q_OBJECT
public:
Calculator(QWidget *parent = 0);
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(); // 当点击添加到内存按钮时触发此槽函数
//! [0]
//! [1]
private:
//! [1] //! [2]
Button *createButton(const QString &text, const char *member); // 创建按钮并返回指针
void abortOperation(); // 中止当前操作
bool calculate(double rightOperand, const QString &pendingOperator); // 计算表达式的结果
//! [2]
//! [3]
double sumInMemory; // 存储在内存中的数值
//! [3] //! [4]
double sumSoFar; // 到目前为止的总和
//! [4] //! [5]
double factorSoFar; // 到目前为止的因子
//! [5] //! [6]
QString pendingAdditiveOperator; // 待处理的加法运算符
//! [6] //! [7]
QString pendingMultiplicativeOperator; // 待处理的乘法运算符
//! [7] //! [8]
bool waitingForOperand; // 是否正在等待操作数
//! [8]
//! [9]
QLineEdit *display; // 显示结果的文本框
//! [9] //! [10]
enum { NumDigitButtons = 10 }; // 数字按钮的数量
Button *digitButtons[NumDigitButtons]; // 数字按钮数组
};
//! [10]
#endif
button.h
#ifndef BUTTON_H // 防止头文件重复包含
#define BUTTON_H
#include <QToolButton> // 引入QToolButton类,用于创建工具按钮
//! [0]
class Button : public QToolButton // 定义一个名为Button的类,继承自QToolButton
{
Q_OBJECT // 声明这是一个Qt对象,以便使用Qt的信号和槽机制
public:
explicit Button(const QString &text, QWidget *parent = 0); // 构造函数,接受一个文本参数和一个可选的父窗口指针
QSize sizeHint() const override; // 重写sizeHint()函数,返回按钮的建议大小
};
//! [0]
#endif // 结束头文件保护宏
button.cpp
#include <QtWidgets>
#include "button.h"
//! [0]
Button::Button(const QString &text, QWidget *parent)
: QToolButton(parent) // 构造函数,初始化父类为传入的parent参数
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); // 设置按钮的大小策略,使其可以自动扩展
setText(text); // 设置按钮的文本内容为传入的text参数
}
//! [0]
//! [1]
QSize Button::sizeHint() const
//! [1] //! [2]
{
QSize size = QToolButton::sizeHint(); // 获取父类QToolButton的默认大小提示
size.rheight() += 40; // 增加按钮的高度,这里加40像素
size.rwidth() = qMax(size.width(), size.height()); // 将按钮的宽度设置为其宽度和高度中的较大值
return size; // 返回调整后的大小
}
//! [2]
calculator.cpp
#include <QtWidgets>
#include <cmath>
#include "button.h"
#include "calculator.h"
//! [0]
Calculator::Calculator(QWidget *parent)
: QWidget(parent)
{
sumInMemory = 0.0; // 初始化内存中的和为0
sumSoFar = 0.0; // 初始化到目前为止的和为0
factorSoFar = 0.0; // 初始化到目前为止的因子为0
waitingForOperand = true; // 等待操作数的标志设置为true
//! [0]
//! [1]
display = new QLineEdit("0"); // 创建一个显示框,初始值为0
//! [1] //! [2]
display->setReadOnly(true); // 设置显示框为只读
display->setAlignment(Qt::AlignRight); // 设置对齐方式为右对齐
display->setMaxLength(15); // 设置显示框的最大长度为15
QFont font = display->font(); // 获取显示框的字体
font.setPointSize(font.pointSize() + 40); // 增加字体大小
display->setFont(font); // 设置新的字体
//! [2]
//! [4]
for (int i = 0; i < NumDigitButtons; ++i) { // 遍历数字按钮
digitButtons[i] = createButton(QString::number(i), SLOT(digitClicked())); // 创建数字按钮并连接到digitClicked槽函数
}
Button *pointButton = createButton(tr("."), SLOT(pointClicked())); // 创建小数点按钮并连接到pointClicked槽函数
Button *changeSignButton = createButton(tr("\302\261"), SLOT(changeSignClicked())); // 创建改变符号按钮并连接到changeSignClicked槽函数
Button *backspaceButton = createButton(tr("Backspace"), SLOT(backspaceClicked())); // 创建退格键按钮并连接到backspaceClicked槽函数
Button *clearButton = createButton(tr("Clear"), SLOT(clear())); // 创建清除按钮并连接到clear槽函数
Button *clearAllButton = createButton(tr("Clear All"), SLOT(clearAll())); // 创建清除所有按钮并连接到clearAll槽函数
Button *clearMemoryButton = createButton(tr("MC"), SLOT(clearMemory())); // 创建清除内存按钮并连接到clearMemory槽函数
Button *readMemoryButton = createButton(tr("MR"), SLOT(readMemory())); // 创建读取内存按钮并连接到readMemory槽函数
Button *setMemoryButton = createButton(tr("MS"), SLOT(setMemory())); // 创建设置内存按钮并连接到setMemory槽函数
Button *addToMemoryButton = createButton(tr("M+"), SLOT(addToMemory())); // 创建添加到内存按钮并连接到addToMemory槽函数
Button *divisionButton = createButton(tr("\303\267"), SLOT(multiplicativeOperatorClicked())); // 创建除法按钮并连接到multiplicativeOperatorClicked槽函数
Button *timesButton = createButton(tr("\303\227"), SLOT(multiplicativeOperatorClicked())); // 创建乘法按钮并连接到multiplicativeOperatorClicked槽函数
Button *minusButton = createButton(tr("-"), SLOT(additiveOperatorClicked())); // 创建减法按钮并连接到additiveOperatorClicked槽函数
Button *plusButton = createButton(tr("+"), SLOT(additiveOperatorClicked())); // 创建加法按钮并连接到additiveOperatorClicked槽函数
Button *squareRootButton = createButton(tr("Sqrt"), SLOT(unaryOperatorClicked())); // 创建平方根按钮并连接到unaryOperatorClicked槽函数
Button *powerButton = createButton(tr("x\302\262"), SLOT(unaryOperatorClicked())); // 创建幂运算按钮并连接到unaryOperatorClicked槽函数
Button *reciprocalButton = createButton(tr("1/x"), SLOT(unaryOperatorClicked())); // 创建倒数按钮并连接到unaryOperatorClicked槽函数
Button *equalButton = createButton(tr("="), SLOT(equalClicked())); // 创建等于按钮并连接到equalClicked槽函数
//! [4]
//! [5]
QGridLayout *mainLayout = new QGridLayout; // 创建一个网格布局
//! [5] //! [6]
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, 4); // 将减法按钮添加到布局中
mainLayout->addWidget(plusButton, 5, 4); // 将加法按钮添加到布局中
mainLayout->addWidget(squareRootButton, 2, 5); // 将平方根按钮添加到布局中
mainLayout->addWidget(powerButton, 3, 5); // 将幂运算按钮添加到布局中
mainLayout->addWidget(reciprocalButton, 4, 5); // 将倒数按钮添加到布局中
mainLayout->addWidget(equalButton, 5, 5); // 将等于按钮添加到布局中
setLayout(mainLayout); // 设置窗口的布局为刚刚创建的网格布局
setWindowTitle(tr("Calculator")); // 设置窗口标题为“计算器”
//! [6]
}
//! [7]
void Calculator::digitClicked()
{
Button *clickedButton = qobject_cast<Button *>(sender()); // 获取发送信号的按钮对象
int digitValue = clickedButton->text().toInt(); // 获取按钮上的数值
// 如果当前显示框为空或者点击的是0,则直接显示该数值
if (display->text() == "0" && digitValue == 0.0)
return;
if (waitingForOperand) {
display->clear(); // 如果正在等待操作数,则清空显示框
waitingForOperand = false;
}
display->setText(display->text() + QString::number(digitValue)); // 在显示框上添加新的数值
}
//! [8]
void Calculator::unaryOperatorClicked()
{
Button *clickedButton = qobject_cast<Button *>(sender()); // 获取发送信号的按钮对象
QString clickedOperator = clickedButton->text(); // 获取按钮上的运算符
double operand = display->text().toDouble(); // 获取显示框上的数值
double result = 0.0;
if (clickedOperator == tr("Sqrt")) {
if (operand < 0.0) {
abortOperation(); // 如果数值小于0,则中止操作
return;
}
result = std::sqrt(operand); // 计算平方根
} else if (clickedOperator == tr("x\302\262")) {
result = std::pow(operand, 2.0); // 计算平方
} else if (clickedOperator == tr("1/x")) {
if (operand == 0.0) {
abortOperation(); // 如果数值为0,则中止操作
return;
}
result = 1.0 / operand; // 计算倒数
}
display->setText(QString::number(result)); // 在显示框上显示结果
waitingForOperand = true; // 设置等待操作数标志为true
}
//! [9]
//! [10]
void Calculator::additiveOperatorClicked()
//! [10] //! [11]
{
Button *clickedButton = qobject_cast<Button *>(sender()); // 获取被点击的按钮对象
QString clickedOperator = clickedButton->text(); // 获取被点击的按钮上的文本,即运算符
double operand = display->text().toDouble(); // 将显示屏上的数字转换为双精度浮点数
//! [11] //! [12]
if (!pendingMultiplicativeOperator.isEmpty()) { // 如果有待处理的乘法运算符
//! [12] //! [13]
if (!calculate(operand, pendingMultiplicativeOperator)) { // 如果计算失败
abortOperation(); // 中止操作
return; // 返回
}
display->setText(QString::number(factorSoFar)); // 将结果显示在显示屏上
operand = factorSoFar; // 更新操作数为结果
factorSoFar = 0.0; // 重置因子
pendingMultiplicativeOperator.clear(); // 清空待处理的乘法运算符
}
//! [13] //! [14]
if (!pendingAdditiveOperator.isEmpty()) { // 如果有待处理的加法运算符
//! [14] //! [15]
if (!calculate(operand, pendingAdditiveOperator)) { // 如果计算失败
abortOperation(); // 中止操作
return; // 返回
}
display->setText(QString::number(sumSoFar)); // 将结果显示在显示屏上
} else {
sumSoFar = operand; // 否则,将操作数设置为结果
}
//! [15] //! [16]
pendingAdditiveOperator = clickedOperator; // 更新待处理的加法运算符为当前点击的运算符
//! [16] //! [17]
waitingForOperand = true; // 设置等待操作数的标志为真
}
//! [17]
//! [18]
void Calculator::multiplicativeOperatorClicked()
{
Button *clickedButton = qobject_cast<Button *>(sender()); // 获取被点击的按钮对象
QString clickedOperator = clickedButton->text(); // 获取被点击的按钮上的文本,即运算符
double operand = display->text().toDouble(); // 将显示屏上的数字转换为双精度浮点数
if (!pendingMultiplicativeOperator.isEmpty()) { // 如果有待处理的乘法运算符
if (!calculate(operand, pendingMultiplicativeOperator)) { // 如果计算失败
abortOperation(); // 中止操作
return; // 返回
}
display->setText(QString::number(factorSoFar)); // 将结果显示在显示屏上
} else {
factorSoFar = operand; // 否则,将操作数设置为结果
}
pendingMultiplicativeOperator = clickedOperator; // 更新待处理的乘法运算符为当前点击的运算符
waitingForOperand = true; // 设置等待操作数的标志为真
}
//! [18]
//! [20]
void Calculator::equalClicked()
{
double operand = display->text().toDouble(); // 将显示屏上的数字转换为双精度浮点数
if (!pendingMultiplicativeOperator.isEmpty()) { // 如果有待处理的乘法运算符
if (!calculate(operand, pendingMultiplicativeOperator)) { // 如果计算失败
abortOperation(); // 中止操作
return; // 返回
}
operand = factorSoFar; // 将结果赋值给操作数
factorSoFar = 0.0; // 重置因子
pendingMultiplicativeOperator.clear(); // 清空待处理的乘法运算符
}
if (!pendingAdditiveOperator.isEmpty()) { // 如果有待处理的加法运算符
if (!calculate(operand, pendingAdditiveOperator)) { // 如果计算失败
abortOperation(); // 中止操作
return; // 返回
}
pendingAdditiveOperator.clear(); // 清空待处理的加法运算符
} else {
sumSoFar = operand; // 否则,将操作数设置为结果
}
display->setText(QString::number(sumSoFar)); // 将结果显示在显示屏上
sumSoFar = 0.0; // 重置求和结果
waitingForOperand = true; // 设置等待操作数的标志为真
}
//! [22]
void Calculator::pointClicked()
{
// 如果等待操作数,将显示屏文本设置为"0"
if (waitingForOperand)
display->setText("0");
// 如果显示屏文本中不包含小数点,则在文本末尾添加一个小数点
if (!display->text().contains('.'))
display->setText(display->text() + tr("."));
// 设置等待操作数标志为false
waitingForOperand = false;
}
//! [22]
//! [24]
void Calculator::changeSignClicked()
{
QString text = display->text();
double value = text.toDouble();
// 如果值为正数,则在文本开头添加负号
if (value > 0.0) {
text.prepend(tr("-"));
}
// 如果值为负数,则移除文本开头的负号
else if (value < 0.0) {
text.remove(0, 1);
}
// 将修改后的文本设置回显示屏
display->setText(text);
}
//! [24]
//! [26]
void Calculator::backspaceClicked()
{
// 如果等待操作数,直接返回
if (waitingForOperand)
return;
QString text = display->text();
// 移除文本末尾的一个字符
text.chop(1);
// 如果文本为空,则将其设置为"0",并将等待操作数标志设置为true
if (text.isEmpty()) {
text = "0";
waitingForOperand = true;
}
// 将修改后的文本设置回显示屏
display->setText(text);
}
//! [26]
//! [28]
void Calculator::clear()
{
// 如果等待操作数,直接返回
if (waitingForOperand)
return;
// 将显示屏文本设置为"0",并将等待操作数标志设置为true
display->setText("0");
waitingForOperand = true;
}
//! [28]
// Calculator类的成员函数clearAll(),用于清除计算器的所有状态
void Calculator::clearAll()
{
sumSoFar = 0.0; // 将累加结果清零
factorSoFar = 0.0; // 将乘除结果清零
pendingAdditiveOperator.clear(); // 清空待处理的加法运算符
pendingMultiplicativeOperator.clear(); // 清空待处理的乘除运算符
display->setText("0"); // 将显示屏显示为"0"
waitingForOperand = true; // 设置等待操作数的标志为true
}
// Calculator类的成员函数clearMemory(),用于清除内存中的值
void Calculator::clearMemory()
{
sumInMemory = 0.0; // 将内存中的值清零
}
// Calculator类的成员函数readMemory(),用于读取内存中的值并显示在显示屏上
void Calculator::readMemory()
{
display->setText(QString::number(sumInMemory)); // 将内存中的值转换为字符串并显示在显示屏上
waitingForOperand = true; // 设置等待操作数的标志为true
}
// Calculator类的成员函数setMemory(),用于将显示屏上的值存入内存中
void Calculator::setMemory()
{
equalClicked(); // 执行等号点击事件
sumInMemory = display->text().toDouble(); // 将显示屏上的值转换为双精度浮点数并存入内存中
}
// Calculator类的成员函数addToMemory(),用于将显示屏上的值加到内存中的值上
void Calculator::addToMemory()
{
equalClicked(); // 执行等号点击事件
sumInMemory += display->text().toDouble(); // 将显示屏上的值转换为双精度浮点数并加到内存中的值上
}
// Calculator类的成员函数createButton(),用于创建一个按钮并将其与指定的成员函数连接起来
Button *Calculator::createButton(const QString &text, const char *member)
{
Button *button = new Button(text); // 创建一个新的按钮
connect(button, SIGNAL(clicked()), this, member); // 将按钮的点击事件与指定的成员函数连接起来
return button; // 返回创建的按钮
}
// Calculator类的成员函数abortOperation(),用于中止当前的操作并将显示屏显示为"####"
void Calculator::abortOperation()
{
clearAll(); // 调用clearAll()函数清除所有状态
display->setText(tr("####")); // 将显示屏显示为"####"
}
// Calculator类的成员函数calculate(),用于根据给定的操作数和运算符进行计算
bool Calculator::calculate(double rightOperand, const QString &pendingOperator)
{
if (pendingOperator == tr("+")) { // 如果待处理的运算符是加法
sumSoFar += rightOperand; // 将右操作数加到累加结果上
} else if (pendingOperator == tr("-")) { // 如果待处理的运算符是减法
sumSoFar -= rightOperand; // 将右操作数从累加结果中减去
} else if (pendingOperator == tr("\303\227")) { // 如果待处理的运算符是乘法
factorSoFar *= rightOperand; // 将右操作数乘以乘除结果
} else if (pendingOperator == tr("\303\267")) { // 如果待处理的运算符是除法
if (rightOperand == 0.0) // 如果右操作数为0
return false; // 返回false表示计算失败
factorSoFar /= rightOperand; // 将右操作数从乘除结果中除以
}
return true; // 返回true表示计算成功
}
main.cpp
#include <QApplication> // 引入Qt框架的QApplication类,用于处理应用程序的事件循环和退出
#include "calculator.h" // 引入名为"calculator.h"的头文件,该文件定义了Calculator类
int main(int argc, char *argv[]) // 主函数,程序的入口点
{
QApplication app(argc, argv); // 创建一个QApplication对象,用于管理应用程序的事件循环和退出
Calculator calc; // 创建一个Calculator对象,用于显示计算器界面
calc.show(); // 调用Calculator对象的show()方法,显示计算器界面
return app.exec(); // 进入应用程序的事件循环,等待用户操作并处理事件
}